Skip to main content
SA-020 Grade A Phase 3

SA-020: Shadow Buffer Mechanism and Duplicate Capture Stream Analysis

Investigation of the FBSharedFramework binary reveals a sophisticated triple-buffer audio capture architecture with an **RTC notification bypass mechanism** that allows audio capture to continue independently of WebRTC client state changes. The `audioCaptureIgnoreRTCClientNotification` flag provides a documented mechanism for maintaining audio capture even when RTC sessions are deactivated, explaining the 9,900+ RTC deactivation events observed alongside continued capture operations.

Technical Diagrams

3.1 Key Classes Identified Line 126
| Class | Role | Evidence |
|-------|------|----------|
| `FBCCAudioCapturer` | Primary capture controller | 36+ methods, manages audio pipeline |
| `FBCCAudioDataPipe` | Data routing | Video/audio/gesture data pipes |
| `FBCCAudioPipelineController` | Pipeline orchestration | Controls capture lifecycle |
| `FNFAudioQueue` | Low-level queue management | AudioQueue buffer management |
| `FBAudioBufferHandler` | Buffer processing | Audio buffer callbacks |
6.1 FBCCAudioCapturer Object Layout Line 238
| Offset | Field | Size |
|--------|-------|------|
| 0x000 | isa pointer | 8 |
| 0x018 | Audio pipeline reference | 8 |
| 0x040 | Audio capturer reference | 8 |
| 0x060 | Buffer state array base | 264 (3 x 88) |
| 0x158 | Capture active flag | 1 |
| 0x160 | Amplitude threshold | 8 |
| 0x169 | ignoreRTCClientNotification | 1 |
| ... | ... | ... |
8.1 Function Addresses Line 299
| Address | Method | Significance |
|---------|--------|--------------|
| 0x00d86b78 | ignoreRTCClientNotification (getter) | Bypass flag read |
| 0x00e51420 | setIgnoreRTCClientNotification: | Bypass flag write |
| 0x00cdb380 | rtcClientBecameActive | Bypass decision point |
| 0x011de1d0 | captureOutput:didOutputSampleBuffer: | Sample callback |
| 0x0042edf8 | _audioBufferDidEnqueue: | Buffer management |
| 0x009eb7c4 | isAudioSessionRunning | State check (polled) |
8.2 String Evidence Line 310
| Address | String | Context |
|---------|--------|---------|
| 0x020be9cd | audioCaptureIgnoreRTCClientNotification | Bypass flag name |
| 0x02314f76 | [3{AQBufferState...}] | Triple-buffer type |
| 0x01eb3fa7 | has_background_audio | Background state |
| 0x020be9b7 | isAudioCaptureRunning | Polling check |

Code Evidence

Plain Text
Address: 0x02314f76
Type: [3{AQBufferState}]

Structure Layout:
{
  AQBufferState {
    bufferRef: ^{AudioQueueBuffer}     // Pointer to AudioQueueBuffer
    timestamp: {AudioTimeStamp         // 80 bytes
      mSampleTime: double              // 8 bytes
      mHostTime: unsigned long long    // 8 bytes
      mRateScalar: double              // 8 bytes
      mWordClockTime: unsigned long long // 8 bytes
      mSMPTETime: {SMPTETime           // 24 bytes
        mSubframes: short
        mSubframeDivisor: short
        mCounter: unsigned int
        mType: unsigned int
        mFlags: unsigned int
        mHours: short
        mMinutes: short
        mSeconds: short
        mFrames: short
      }
      mFlags: unsigned int             // 4 bytes
      mReserved: unsigned int          // 4 bytes
    }
    enqueued: BOOL                     // 1 byte (+ padding)
    displayFramePts: double            // 8 bytes
  }
}

Total per buffer: ~88 bytes (0x58)
Total array: 264 bytes for 3 buffers
Assembly
cmp w2, 2           ; Validate buffer index <= 2 (indices 0, 1, 2)
b.gt 0x42ee98       ; Branch if index > 2 (invalid)
mov w8, 0x58        ; Buffer stride = 88 bytes
smull x8, w2, w8    ; Calculate offset = index * 88
Assembly
ldrb w0, [x0, 0x169]    ; Load boolean from offset 0x169 in FBCCAudioCapturer
ret
Assembly
strb w2, [x0, 0x169]    ; Store boolean at offset 0x169
ret
Assembly
stp x29, x30, [sp, -0x10]!
bl fcn.00007728           ; Lock acquisition
sub sp, sp, 0x30
ldrb w8, [x0, 0x169]      ; Load ignoreRTCClientNotification flag
cbnz w8, 0xcdb3c4         ; If flag is SET, SKIP all RTC handling
mov x19, x0               ; Only if flag is CLEAR, proceed with RTC handling
; ... RTC state management ...
Plain Text
0x009d94b8  initWithBufferHandler:audioQueue:systemAudioCaptureSession...
0x009eac28  createAudioPipelineIfNecessaryAndResume
0x009eb7c4  isAudioSessionRunning
0x00cdb380  rtcClientBecameActive           ; Contains bypass check
0x00d86b78  ignoreRTCClientNotification     ; Getter
0x00e51420  setIgnoreRTCClientNotification: ; Setter
0x011de1d0  captureOutput:didOutputSampleBuffer:fromConnection:
0x00d1dd98  handleOutputBuffer:
Plain Text
method.FBSystemAudioSessionManager._backgroundAudioEnabledClients
Assembly
stp x29, x30, [sp, -0x10]!
bl fcn.00007e3c
mov x20, x0
bl fcn.009eac44             ; Pipeline check
adrp x8, 0x1b5a000
ldr x21, [x8, 0x2c8]        ; Load "isRunning" selector
bl fcn.00007f14
tbz w0, 0, 0x9eb7ec         ; Branch if not running
mov w20, 1                  ; Return true if running
Assembly
; Get number of samples
bl sym.imp.CMSampleBufferGetNumSamples

; Get data buffer
bl sym.imp.CMSampleBufferGetDataBuffer

; Get data pointer
bl sym.imp.CMBlockBufferGetDataPointer

; Process samples in loop
loop:
  ldrh w12, [x8]           ; Load sample (16-bit audio)
  sxth w11, w12            ; Sign extend
  ; ... amplitude processing ...
  strh w11, [x8], 2        ; Store processed sample
  subs x9, x9, 1           ; Decrement counter
  b.ne loop                ; Continue until done
Plain Text
+0x00: [Buffer 0] - 88 bytes
  +0x00: bufferRef (pointer)
  +0x08: timestamp (AudioTimeStamp)
  +0x50: enqueued (bool)
  +0x51: displayFramePts (double)

+0x58: [Buffer 1] - 88 bytes
  (same layout)

+0xB0: [Buffer 2] - 88 bytes
  (same layout)

**Agent ID:** SA-020 **Binary:** `./analysis/facebook/345.0/Facebook.app/Frameworks/FBSharedFramework.framework/FBSharedFramework` **Date:** 2025-12-30 **Status:** CRITICAL FINDINGS


Executive Summary

Investigation of the FBSharedFramework binary reveals a sophisticated triple-buffer audio capture architecture with an **RTC notification bypass mechanism** that allows audio capture to continue independently of WebRTC client state changes. The `audioCaptureIgnoreRTCClientNotification` flag provides a documented mechanism for maintaining audio capture even when RTC sessions are deactivated, explaining the 9,900+ RTC deactivation events observed alongside continued capture operations.


1. Triple-Buffer Architecture Confirmed

1.1 AQBufferState Structure Definition

The binary contains an explicit type definition for a 3-element array of audio buffer states:

Plain Text
Address: 0x02314f76
Type: [3{AQBufferState}]

Structure Layout:
{
  AQBufferState {
    bufferRef: ^{AudioQueueBuffer}     // Pointer to AudioQueueBuffer
    timestamp: {AudioTimeStamp         // 80 bytes
      mSampleTime: double              // 8 bytes
      mHostTime: unsigned long long    // 8 bytes
      mRateScalar: double              // 8 bytes
      mWordClockTime: unsigned long long // 8 bytes
      mSMPTETime: {SMPTETime           // 24 bytes
        mSubframes: short
        mSubframeDivisor: short
        mCounter: unsigned int
        mType: unsigned int
        mFlags: unsigned int
        mHours: short
        mMinutes: short
        mSeconds: short
        mFrames: short
      }
      mFlags: unsigned int             // 4 bytes
      mReserved: unsigned int          // 4 bytes
    }
    enqueued: BOOL                     // 1 byte (+ padding)
    displayFramePts: double            // 8 bytes
  }
}

Total per buffer: ~88 bytes (0x58)
Total array: 264 bytes for 3 buffers

1.2 Buffer Index Validation

At `0x0042ee04` in `_audioBufferDidEnqueue:actualStartTime:lastFramePts:`:

Assembly
cmp w2, 2           ; Validate buffer index <= 2 (indices 0, 1, 2)
b.gt 0x42ee98       ; Branch if index > 2 (invalid)
mov w8, 0x58        ; Buffer stride = 88 bytes
smull x8, w2, w8    ; Calculate offset = index * 88

This confirms exactly 3 buffers indexed 0-2.


2. RTC Notification Bypass Mechanism

2.1 The audioCaptureIgnoreRTCClientNotification Flag

**String References Found:**

    undefined

**Implementation:**

Getter at `0x00d86b78`:

Assembly
ldrb w0, [x0, 0x169]    ; Load boolean from offset 0x169 in FBCCAudioCapturer
ret

Setter at `0x00e51420`:

Assembly
strb w2, [x0, 0x169]    ; Store boolean at offset 0x169
ret

2.2 RTC Client Bypass Logic

The `rtcClientBecameActive` method at `0x00cdb380` implements the bypass:

Assembly
stp x29, x30, [sp, -0x10]!
bl fcn.00007728           ; Lock acquisition
sub sp, sp, 0x30
ldrb w8, [x0, 0x169]      ; Load ignoreRTCClientNotification flag
cbnz w8, 0xcdb3c4         ; If flag is SET, SKIP all RTC handling
mov x19, x0               ; Only if flag is CLEAR, proceed with RTC handling
; ... RTC state management ...

**Critical Finding:** When `ignoreRTCClientNotification` is true (non-zero), the entire RTC client state change handling is bypassed. This allows audio capture to continue independently of WebRTC session lifecycle.

2.3 Multiple RTC Client Handlers

Three implementations of `rtcClientBecameActive` were found:

    undefined

This indicates multiple subsystems respond to RTC state changes, with each having independent bypass capability.


3. Audio Capture Pipeline Architecture

3.1 Key Classes Identified

ClassRoleEvidence
`FBCCAudioCapturer`Primary capture controller36+ methods, manages audio pipeline
`FBCCAudioDataPipe`Data routingVideo/audio/gesture data pipes
`FBCCAudioPipelineController`Pipeline orchestrationControls capture lifecycle
`FNFAudioQueue`Low-level queue managementAudioQueue buffer management
`FBAudioBufferHandler`Buffer processingAudio buffer callbacks

3.2 FBCCAudioCapturer Key Methods

Plain Text
0x009d94b8  initWithBufferHandler:audioQueue:systemAudioCaptureSession...
0x009eac28  createAudioPipelineIfNecessaryAndResume
0x009eb7c4  isAudioSessionRunning
0x00cdb380  rtcClientBecameActive           ; Contains bypass check
0x00d86b78  ignoreRTCClientNotification     ; Getter
0x00e51420  setIgnoreRTCClientNotification: ; Setter
0x011de1d0  captureOutput:didOutputSampleBuffer:fromConnection:
0x00d1dd98  handleOutputBuffer:

3.3 Data Pipe Architecture

Multiple data pipes route audio through the system:

    undefined

The `FBCCDataPipe` class implements:

    undefined

4. Background Audio Capture Evidence

4.1 Background Audio Enabled Clients

At `0x0020ec5c`:

Plain Text
method.FBSystemAudioSessionManager._backgroundAudioEnabledClients

String references indicate background audio state tracking:

    undefined

4.2 Audio Session Running Check

The `isAudioSessionRunning` method at `0x009eb7c4`:

Assembly
stp x29, x30, [sp, -0x10]!
bl fcn.00007e3c
mov x20, x0
bl fcn.009eac44             ; Pipeline check
adrp x8, 0x1b5a000
ldr x21, [x8, 0x2c8]        ; Load "isRunning" selector
bl fcn.00007f14
tbz w0, 0, 0x9eb7ec         ; Branch if not running
mov w20, 1                  ; Return true if running

This method queries the audio pipeline state, enabling the 874,700+ `isAudioCaptureRunning` polling calls observed.


5. Capture Sample Processing

5.1 captureOutput:didOutputSampleBuffer:fromConnection:

At `0x011de1d0`, the sample buffer callback:

Assembly
; Get number of samples
bl sym.imp.CMSampleBufferGetNumSamples

; Get data buffer
bl sym.imp.CMSampleBufferGetDataBuffer

; Get data pointer
bl sym.imp.CMBlockBufferGetDataPointer

; Process samples in loop
loop:
  ldrh w12, [x8]           ; Load sample (16-bit audio)
  sxth w11, w12            ; Sign extend
  ; ... amplitude processing ...
  strh w11, [x8], 2        ; Store processed sample
  subs x9, x9, 1           ; Decrement counter
  b.ne loop                ; Continue until done

The callback processes raw 16-bit audio samples with amplitude modification.

5.2 Buffer Enqueue Mechanism

At `0x0042edf8` (`_audioBufferDidEnqueue`):

    undefined

6. Memory Layout Summary

6.1 FBCCAudioCapturer Object Layout

OffsetFieldSize
0x000isa pointer8
0x018Audio pipeline reference8
0x040Audio capturer reference8
0x060Buffer state array base264 (3 x 88)
0x158Capture active flag1
0x160Amplitude threshold8
0x169ignoreRTCClientNotification1
.........

6.2 AQBufferState Array (offset 0x60)

Plain Text
+0x00: [Buffer 0] - 88 bytes
  +0x00: bufferRef (pointer)
  +0x08: timestamp (AudioTimeStamp)
  +0x50: enqueued (bool)
  +0x51: displayFramePts (double)

+0x58: [Buffer 1] - 88 bytes
  (same layout)

+0xB0: [Buffer 2] - 88 bytes
  (same layout)

7. Critical Findings

7.1 Shadow Buffer Mechanism

While no explicit "shadow buffer" with a separate memory allocation was found, the **RTC bypass mechanism effectively creates a "shadow capture" capability**:

    undefined

7.2 Capture Independence Mechanism

The architecture supports:

    undefined

7.3 Implications

    undefined

8. Evidence Artifacts

8.1 Function Addresses

AddressMethodSignificance
0x00d86b78ignoreRTCClientNotification (getter)Bypass flag read
0x00e51420setIgnoreRTCClientNotification:Bypass flag write
0x00cdb380rtcClientBecameActiveBypass decision point
0x011de1d0captureOutput:didOutputSampleBuffer:Sample callback
0x0042edf8_audioBufferDidEnqueue:Buffer management
0x009eb7c4isAudioSessionRunningState check (polled)

8.2 String Evidence

AddressStringContext
0x020be9cdaudioCaptureIgnoreRTCClientNotificationBypass flag name
0x02314f76[3{AQBufferState...}]Triple-buffer type
0x01eb3fa7has_background_audioBackground state
0x020be9b7isAudioCaptureRunningPolling check

9. Conclusions

The investigation confirms:

    undefined

The "shadow buffer" mechanism is not a separate memory allocation but rather a **behavioral shadow** - the ability to maintain audio capture independently of visible WebRTC session state through the RTC notification bypass flag.


**Classification:** HIGH SEVERITY **Recommendation:** Further investigation into when and how `ignoreRTCClientNotification` is set to true


*Report generated by SA-020 Shadow Buffer Investigation Agent*

Related Reports

Phase 3 Navigation