[CUDA:Bugfix] Fix int32 overflow in im2col packing for large inputs (NaN/garbage output on conv layers with e*l > INT32_MAX) - #4817
Conversation
…NaN/garbage output on conv layers with e*l > INT32_MAX) On layers where (output pixels) * (packed input channels) exceeds INT32_MAX (e.g. PP-OCRv4 server det, 1280x1920 input, 256ch 9x9 conv: 153600 * 20736 = 3,185,049,600), two int32 multiplications overflow: 1. callIm2ColPack(): size_t maxCount = e * lp -> int*int overflow produced a garbage grid dimension, so cudaLaunchKernel failed with cudaErrorInvalidValue. checkKernelErrors is compiled out in release builds, so the launch failure was silent and downstream layers consumed uninitialized memory (NaN prob maps, scrambled detections). 2. Im2Col_FilterC_Vec4/FilterC kernels: size_t dst_offset = eIndex * l_p + ... -> int*int overflow corrupted the im2col buffer write addressing, silently producing wrong values even when the launch itself succeeded. Fix: promote the multiplications to size_t. Also fixed the same pattern in ConvCutlassExecution.cu, DeconvSingleInputExecution.cu and MultiInputConvExecution.cu. Verified on RTX A10G (sm_86, CUDA 13.0, MNN 3.6.1): - PP-OCRv4 server det at 1280x1920: output maxdiff vs CPU drops from 1.0 (garbage) to 0.06-0.15 (fp16-mix rounding), NaN count 96170 -> 0. - Overflow boundary confirmed: layer e*lp 2.12e9 passes, 2.34e9 fails before the patch, matching the INT32_MAX threshold exactly.
|
Thanks for the contribution, and for tracking down this overflow — the direction is right and it continues the 1. Three of the six changes are truncated back to
|
…ard non-vectorized im2col path - callFloat2Half / callFloat2BFloat16: widen count and thread_count to size_t (declaration, definition, kernel launch). The three call sites that pass freshly-computed size_t maxCount (ConvCutlassExecution, DeconvSingleInputExecution, MultiInputConvExecution) were narrowed back to int at the call boundary, making those widenings no-ops. - callIm2ColPack: the non-vectorized Im2Col_FilterC path relies on int-based DivModFast indexing, which becomes invalid once maxCount exceeds 2^31-1 (indexO itself truncates, magic division no longer valid). Fail loudly with an error message instead of silently corrupting output. The Vec4 path (ic%4==0) is unaffected: its indexO = e*lp/4 stays below 2^31 while dst_offset exceeds it, which is exactly the regime fixed by the size_t dst_offset widening.
|
Thank you for the careful review — all points addressed in f338f39. 1. Widened 2. Added a guard in 3. Verification data (A10G 24GB, SM86, CUDA 13, fp32 inference via Reproducing shape: PP-OCRv4_server_det, the 256ch 9x9 conv layer (
Full PP-OCR detection+recognition matrix on this build: the previously failing server-det CUDA cells went from 0% to 191/224 passing against the paddle CPU baseline; the 33 remaining deltas are single-character recognition differences from fp32 reduction order (prob corr 1.000000), unrelated to this fix. Re-ran normal-size inputs (mobile/tiny det + rec across 18 languages) after the widening — no behavioral change, as expected for pure integer widening. Worth noting for future work: this fix does not address the separate issue that the cutlass path materializes the full im2col buffer on the device ( |
Problem
On CUDA, conv layers whose
(output pixels) x (packed input channels)exceeds INT32_MAX silently produce garbage output (NaN probability maps, scrambled detections). We hit this with PP-OCR server segmentation models on 1280x1920 inputs (e.g. a 256-channel 9x9 conv at 320x480 output: 153600 x 20736 = 3,185,049,600 > 2^31).Root cause - two int32 multiplication overflows
callIm2ColPack()(ConvBaseKernel.cu):size_t maxCount = e * lp;-eandlpareint; the product overflows before the assignment tosize_t. The garbage value becomes the grid dimension, socudaLaunchKernelfails withcudaErrorInvalidValue. SincecheckKernelErrorsis compiled out in release builds, the failure is silent and downstream layers consume uninitialized memory.Im2Col_FilterC_Vec4/Im2Col_FilterCkernels:size_t dst_offset = eIndex * l_p + kI * ic + iz;- the RHS is computed entirely inintand overflows before promotion, corrupting the im2col buffer write addressing even when the launch succeeds.Fix
Promote the multiplications to
size_t(cast operands before the multiply). Same-pattern overflows fixed inConvCutlassExecution.cu,DeconvSingleInputExecution.cu,MultiInputConvExecution.cu.Verification (RTX A10G, sm_86, CUDA 13.0, MNN master @ cda4a6f (rebased from 3.6.1 d407447))