vercel-labs / vercel-labs/native

macOS: OOB panic in NativeSdkSpectrumComputeBands when sample rate < 32 kHz (index past Nyquist)

Open Beginner friendly
#108 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Zig
Stars
7.7k
Forks
314
Avg merge
5h
Merged PRs (30d)
13

Description

Summary

NativeSdkSpectrumComputeBands in src/platform/macos/appkit_host.m crashes with an index-out-of-bounds panic when the audio sample rate is low enough that the fixed 50 Hz–16 kHz band range extends past the Nyquist frequency (e.g. 16 kHz sample rate, common for VoIP/telephony audio).

thread 9178883 panic: index 1036 out of bounds for type 'float[1024]'
src/platform/macos/appkit_host.m:8595:17 in NativeSdkSpectrumComputeBands
            if (power[bin] > peak) peak = power[bin];
src/platform/macos/appkit_host.m:8743:10 in -[NativeSdkAppKitHost audioSpectrumTimerFired:]
    if (!NativeSdkSpectrumComputeBands(state, self.audioSpectrumFft, window, event.audio_bands)) return;

Environment

  • @native-sdk/cli 0.4.0 (@native-sdk/cli-darwin-arm64)
  • macOS (Darwin 25.5.0), Apple Silicon
  • Crash observed while running an app via native dev with audio playing at a 16 kHz sample rate (SIP/VoIP call audio)

Root cause

In the band-bucketing loop (appkit_host.m around line 8586):

int low_bin = (int)(low_hz / hz_per_bin);
int high_bin = (int)ceil(high_hz / hz_per_bin);
if (low_bin < 1) low_bin = 1;
if (high_bin > NATIVE_SDK_SPECTRUM_FFT_SIZE / 2 - 1) high_bin = NATIVE_SDK_SPECTRUM_FFT_SIZE / 2 - 1;
if (high_bin < low_bin) high_bin = low_bin;   // <-- undoes the clamp above
float peak = 0.0f;
for (int bin = low_bin; bin <= high_bin; bin += 1) {
    if (power[bin] > peak) peak = power[bin];  // <-- OOB read
}

Two problems combine:

  1. low_bin is clamped only at the bottom (< 1), never against the top of the power[FFT_SIZE / 2] array.
  2. if (high_bin < low_bin) high_bin = low_bin; re-raises high_bin above the top clamp applied on the previous line.

With sample_rate = 16000 and FFT_SIZE = 2048, hz_per_bin ≈ 7.8125. Bands whose low_hz exceeds the 8 kHz Nyquist produce low_bin ≥ 1024 (first offender lands at 1036, matching the panic), high_bin is clamped to 1023, then re-raised to low_bin, and the peak loop reads power[1036] out of bounds.

Any sample rate below 32 kHz can trigger this, since the top band edge is fixed at NATIVE_SDK_SPECTRUM_HIGH_HZ = 16 kHz.

Reproduction

  1. Build any app that enables the audio spectrum feed.
  2. Play audio whose engine sample rate is 16 kHz (e.g. wideband VoIP audio).
  3. Wait for audioSpectrumTimerFired: to run once enough samples are buffered — the app panics.

Suggested fix

Clamp low_bin against the top of the array as well, and skip bands entirely above Nyquist rather than folding them onto the last bin, e.g.:

const int max_bin = NATIVE_SDK_SPECTRUM_FFT_SIZE / 2 - 1;
int low_bin = (int)(low_hz / hz_per_bin);
int high_bin = (int)ceil(high_hz / hz_per_bin);
if (low_bin < 1) low_bin = 1;
if (low_bin > max_bin) {           // band entirely above Nyquist
    bands[band] = 0;               // silence, matches the floor-dB mapping
    continue;
}
if (high_bin > max_bin) high_bin = max_bin;
if (high_bin < low_bin) high_bin = low_bin;

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/platform/macos/appkit_host.m at NativeSdkSpectrumComputeBands and follow its call from audioSpectrumTimerFired:. Verify the band-bucketing bounds for sample rates below 32 kHz, including bands above Nyquist. Done means low-rate audio spectrum processing no longer reads past the power array and above-Nyquist bands produce silence.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, objective-c
Domain
audio-video-rtc, desktop
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.