Skip to content

fix(FIR): size q15 state buffer for ARM_MATH_LOOPUNROLL, not just ARM_MATH_DSP#317

Merged
christophe0606 merged 2 commits into
ARM-software:mainfrom
94xhn:fix/fir-q15-state-buffer-size-loopunroll
Jul 17, 2026
Merged

fix(FIR): size q15 state buffer for ARM_MATH_LOOPUNROLL, not just ARM_MATH_DSP#317
christophe0606 merged 2 commits into
ARM-software:mainfrom
94xhn:fix/fir-q15-state-buffer-size-loopunroll

Conversation

@94xhn

@94xhn 94xhn commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #305.

Problem

arm_fir_init_q15() only allocates the larger (numTaps+blockSize) state buffer when ARM_MATH_DSP is defined; otherwise it allocates the smaller (numTaps+blockSize-1), per the function's own documentation.

arm_fir_q15()'s non-MVE processing function has a separate ARM_MATH_LOOPUNROLL-gated code path (processes 4 samples at a time using read_q15x2_ia()/__SMLALD()/__PKHBT()) that reads up to state-buffer index numTaps+blockSize-1 - one element past what's allocated whenever ARM_MATH_LOOPUNROLL is defined without ARM_MATH_DSP. This is a real, reachable configuration: read_q15x2_ia()/__SMLALD()/__PKHBT() all have portable non-hardware-DSP fallback implementations (in Include/arm_math_memory.h / Include/dsp/none.h), so ARM_MATH_LOOPUNROLL doesn't itself require ARM_MATH_DSP to compile or run - it just happens that most real projects define both together.

Verification

I extracted the exact access pattern of arm_fir_q15()'s loop-unrolled scalar path (read_q15x2_ia/__SMLALD/__SMLALDX/__PKHBT, matching the portable fallback definitions) into a standalone host reproduction, and placed the state buffer immediately before a PAGE_NOACCESS guard page (VirtualAlloc) so any read past the documented allocation size faults immediately instead of silently touching adjacent heap memory:

  • With the current (numTaps+blockSize-1)-sized buffer (the un-fixed !ARM_MATH_DSP sizing): the reproduction crashes with an access violation (STATUS_ACCESS_VIOLATION, 0xC0000005).
  • With a (numTaps+blockSize)-sized buffer (the ARM_MATH_DSP-defined sizing): the reproduction completes cleanly.

This confirms the out-of-bounds read is real and reachable, not just a theoretical pointer-arithmetic concern.

Fix

Changed arm_fir_init_q15()'s sizing condition from #if defined(ARM_MATH_DSP) to #if defined(ARM_MATH_DSP) || defined(ARM_MATH_LOOPUNROLL), so the larger buffer is allocated whenever the loop-unrolled path is actually in use, regardless of whether hardware DSP instructions or their software fallback are what's actually executing it. Updated the docstring to match.

Scope note

I checked the q31/q7/f32 FIR init functions for the same pattern - arm_fir_init_q31.c and arm_fir_init_f32.c have additional, different conditional sizing logic (not a simple ARM_MATH_DSP gate), and arm_fir_init_q7.c doesn't have this ARM_MATH_DSP-gated branch at all - so I did not attempt to extend this same fix to them without separately, individually verifying each one, which is outside the scope of this specific issue.

Disclosure

Generative AI (Claude) was used to help investigate this issue, implement, and verify this fix. All changes were reviewed by me before submission.

…_MATH_DSP

arm_fir_init_q15() only allocates the larger (numTaps+blockSize)
state buffer when ARM_MATH_DSP is defined, otherwise it allocates the
smaller (numTaps+blockSize-1).

arm_fir_q15()'s non-MVE processing function has a separate
ARM_MATH_LOOPUNROLL-gated code path (4-sample-at-a-time, using
read_q15x2_ia()/__SMLALD()/__PKHBT(), which all have portable
non-hardware-DSP fallback implementations and so do not themselves
require ARM_MATH_DSP) that reads up to index numTaps+blockSize-1 from
the state buffer - one element past what's allocated when
ARM_MATH_LOOPUNROLL is defined without ARM_MATH_DSP, an out-of-bounds
read.

Verified with a standalone host reproduction of the exact
read_q15x2_ia/__SMLALD/__PKHBT access pattern, placing the state
buffer immediately before a PAGE_NOACCESS guard page: with the current
(numTaps+blockSize-1)-sized buffer the reproduction crashes with an
access violation; sized to (numTaps+blockSize) it completes cleanly.

Fix: allocate the larger buffer whenever ARM_MATH_DSP *or*
ARM_MATH_LOOPUNROLL is defined, matching what arm_fir_q15()'s
loop-unrolled path actually needs regardless of whether hardware DSP
instructions or their software fallback are in use.

Fixes ARM-software#305

Signed-off-by: 94xhn <87560781+94xhn@users.noreply.github.com>

@christophe0606 christophe0606 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a few comments to address.

Comment thread Source/FilteringFunctions/arm_fir_init_q15.c Outdated
Comment thread Source/FilteringFunctions/arm_fir_init_q15.c Outdated
@christophe0606 christophe0606 added bug Something isn't working good first issue Good for newcomers labels Jul 16, 2026
arm_fir_q15 only exercises the extra state slot in its ARM_MATH_LOOPUNROLL path. Restrict the initialization rule and documentation to that tested configuration so ARM_MATH_DSP alone keeps the normal state size.

Constraint: ARM_MATH_DSP is not a tested arm_fir_q15 execution path

Rejected: Keep the combined macro guard | overstates the implementation and test coverage

Confidence: high

Scope-risk: narrow

Directive: Tie q15 state sizing to the paths actually compiled in arm_fir_q15

Tested: MinGW and WSL full CMSISDSP builds 518/518; strict host FIR harness; WSL ASan

Not-tested: ARM_MATH_DSP-only path, per maintainer direction
@christophe0606
christophe0606 merged commit 918014f into ARM-software:main Jul 17, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working good first issue Good for newcomers

Projects

None yet

2 participants