Skip to content

Fix out-of-bounds write in FastFourierTransform constructor for order=0#2597

Merged
lballabio merged 2 commits into
lballabio:masterfrom
metsw24-max:fft-order-zero-oob
May 25, 2026
Merged

Fix out-of-bounds write in FastFourierTransform constructor for order=0#2597
lballabio merged 2 commits into
lballabio:masterfrom
metsw24-max:fft-order-zero-oob

Conversation

@metsw24-max
Copy link
Copy Markdown
Contributor

Summary

The FastFourierTransform(std::size_t order) constructor in
ql/math/fastfouriertransform.hpp allocates cs_ and sn_ with size
order and then unconditionally writes to cs_[order - 1] and
sn_[order - 1]. When order == 0 both vectors are empty and the
indexing wraps to size_t(-1), producing an out-of-bounds heap write.

How to reproduce

FastFourierTransform::min_order(1) returns 0, so a caller that
chains it straight into the constructor for a single-element transform
hits the bug:

#include <ql/math/fastfouriertransform.hpp>
int main() { QuantLib::FastFourierTransform fft(0); }

With a debug-checked standard library (-D_LIBCPP_DEBUG=1 /
-D_GLIBCXX_DEBUG) this segfaults (exit 139). Under release builds it
silently scribbles past the end of the allocator's bookkeeping for the
empty std::vector storage.

Affected lines (pre-patch):

ql/math/fastfouriertransform.hpp:47
        FastFourierTransform(std::size_t order)
        : cs_(order), sn_(order) {
            std::size_t m = static_cast<std::size_t>(1) << order;
            cs_[order - 1] = std::cos (2 * M_PI / m);   // OOB when order==0
            sn_[order - 1] = std::sin (2 * M_PI / m);   // OOB when order==0
            for (std::size_t i = order - 1; i > 0; --i) {
                ...

The fix

A transform of length 1 == (1 << 0) reduces to a copy and needs no
twiddle factors. The existing transform loop body
for (s = 1; s <= order; ++s) { ... } already does the right thing
for order == 0 (it iterates zero times). The only piece that misbehaves
is the precomputation in the constructor, so early-return there when
order == 0. The change is local to the callee and does not affect any
other code path.

Test / build evidence

Built and ran a small program that exercises both the previously-crashing
order == 0 path and a regression case for order == 3:

ok: order=0
ok: order=0 transform
ok: order=3 transform

Compiled with -D_LIBCPP_DEBUG=1 to catch the OOB write; the unpatched
header exits 139, the patched header exits 0 and produces the expected
DFT bin values (first bin == sum of inputs).

The constructor allocated cs_ and sn_ with size 'order' and then
unconditionally wrote to cs_[order-1] and sn_[order-1]. When 'order'
is 0 (e.g. when callers chain FastFourierTransform::min_order(1),
which returns 0), both vectors are empty and the indexing wraps to
size_t(-1), producing an out-of-bounds write that segfaults under
debug-iterator-checked standard libraries.

A transform of size 1 == 1<<0 reduces to a copy and needs no twiddle
factors, so skip the setup when order is 0. The transform loop body
'for (s = 1; s <= order; ++s)' already handles the order==0 case
correctly.
@boring-cyborg
Copy link
Copy Markdown

boring-cyborg Bot commented May 22, 2026

Thanks for opening this pull request! It might take a while before we look at it, so don't worry if there seems to be no feedback. We'll get to it.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 22, 2026

CLA assistant check
All committers have signed the CLA.

@lballabio
Copy link
Copy Markdown
Owner

Thanks! Can you add a small test case?

@metsw24-max
Copy link
Copy Markdown
Contributor Author

Thanks! Can you add a small test case?

i added a small test

@lballabio
Copy link
Copy Markdown
Owner

Great, thanks.

@coveralls
Copy link
Copy Markdown

Coverage Status

coverage: 74.637% (-0.001%) from 74.638% — metsw24-max:fft-order-zero-oob into lballabio:master

@lballabio lballabio merged commit e9600cd into lballabio:master May 25, 2026
45 checks passed
@boring-cyborg
Copy link
Copy Markdown

boring-cyborg Bot commented May 25, 2026

Congratulations on your first merged pull request!

@lballabio lballabio changed the title Fix out-of-bounds write in FastFourierTransform ctor for order=0 Fix out-of-bounds write in FastFourierTransform constructor for order=0 May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants