Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH sbc] sbc: fix 1-byte heap-OOB read in sbc_unpack_frame_internal
       [not found] <1660735014.5089519.1785359719739.ref@mail.yahoo.com>
@ 2026-07-29 21:15 ` Oğuzhan Akkaya
  0 siblings, 0 replies; only message in thread
From: Oğuzhan Akkaya @ 2026-07-29 21:15 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org; +Cc: luiz.dentz@gmail.com, marcel@holtmann.org


[-- Attachment #1.1: Type: text/plain, Size: 2700 bytes --]

 Hi BlueZ maintainers,
While fuzzing libsbc with a libFuzzer + ASan harness exercising thepublic sbc_decode() entry point, I found a 1-byte heap-buffer-overflowREAD in sbc_unpack_frame_internal() at sbc/sbc.c:489. The off-by-one isin the bound check at line 486:
    for (bit = 0; bit < bits[ch][sb]; bit++) {        if (consumed > len * 8)        <-- should be `>=`            return -1;
        if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)            audio_sample |= 1 << (bits[ch][sb] - bit - 1);
        consumed++;    }
When `consumed == len * 8` the guard does not fire and the next linereads `data[len]`, one byte past the input buffer.
The bug has been live since commit feb1bd57 (2011-10-19) — surviving theadjacent hardening in b3ae4260 ("sbc: Fix stack overflow read insbc_crc8.", 2018) which fixed a similar pattern in the CRC helper.
Reproducer:
- Standalone PoC (compiles against system libsbc, ASan-enabled): attached  as poc_standalone.c- A 14-byte input is enough to reach the off-by-one:    9c bd b0 36 7d f2 f2 f2 f2 f2 f2 f2 f2 f2
The proposed fix is a single-character change (`>` → `>=`); patchattached as 0001-sbc-fix-1-byte-heap-OOB-read-in-sbc_unpack_frame_int.patch.
Verified on git HEAD b3deb8a ("Release 2.2"). After the patch the sameinput is rejected cleanly by sbc_decode() (returns -1).
Impact assessment:
- Reachable via any A2DP audio frame (the SBC codec is mandatory for  A2DP); also reachable via libsbc users such as PulseAudio/PipeWire BT  modules, GStreamer's sbcparse/sbcdec, ofono, and most embedded BT  audio gateways that link libsbc.- Primitive is a 1-byte out-of-bounds READ; in a long-lived audio  pipeline the chunk neighborhood is attacker-shapeable across frames,  so the strongest immediate use is a probabilistic info-leak / heap  layout oracle. CVSSv3.1 (proposed):    AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N  (4.3 Medium)- AOSP/Android does NOT ship libsbc — Android's BT stack uses the OI  Codec at bluedroid/embdrv/sbc/, which is a structurally different  implementation and not affected by this exact bug.
This issue has been assigned CVE-2026-16473 (via Red Hat SecurityResponse Team). Public references:
- https://access.redhat.com/security/cve/CVE-2026-16473 - https://bugzilla.redhat.com/show_bug.cgi?id=2503650
I am including the id here for the commit record; please reference itwhen the fix is merged.
Thanks,
---Attachments:
- 0001-sbc-fix-1-byte-heap-OOB-read-in-sbc_unpack_frame_int.patch- poc_standalone.c


Oguzhan Akkaya

Security Engineer

Per.Mail: oakkaya@ymail.com

LinkedIn | Twitter | Reddit | Github | Researchgate |


[-- Attachment #1.2: Type: text/html, Size: 5530 bytes --]

[-- Attachment #2: 0001-sbc-fix-1-byte-heap-OOB-read-in-sbc_unpack_frame_int.patch --]
[-- Type: application/octet-stream, Size: 3358 bytes --]

From f4a2bc64faee408f925ae7800fc18a9213e1cc77 Mon Sep 17 00:00:00 2001
From: Oğuzhan Akkaya <oakkaya@ymail.com>
Date: Wed, 29 Jul 2026 16:27:03 -0400
Subject: [PATCH sbc] sbc: fix 1-byte heap-OOB read in sbc_unpack_frame_internal
To: linux-bluetooth@vger.kernel.org
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: marcel@holtmann.org

The per-sample bit-reader at sbc.c:485-493 has an off-by-one bound check
that allows the buffer-end to be reached and one byte to be read past
the attacker-controlled SBC frame buffer.

When the cumulative `consumed` bit-position reaches len*8 exactly, the
guard `if (consumed > len * 8)` does not fire, and the next statement
indexes `data[consumed >> 3]` == `data[len]`, one byte past the
end of the input buffer. AddressSanitizer reports:

  ERROR: AddressSanitizer: heap-buffer-overflow
  READ of size 1
    #0 sbc_unpack_frame_internal sbc.c:489
    #1 sbc_decode                sbc.c:1225

The bug has been present since commit feb1bd57 (2011-10-19, 'sbc:
Reduce for-loop induced indentation in sbc_unpack_frame'). It was
not addressed by the adjacent hardening in b3ae4260 ('sbc: Fix stack
overflow read in sbc_crc8.', 2018) which fixed a similar 1-byte
over-read in sbc_crc8.

The fix changes the comparison to `>=` so that the guard fires when
`consumed` would reach the byte beyond the buffer.

CVE: CVE-2026-16473
Signed-off-by: Oğuzhan Akkaya <oakkaya@ymail.com>
---
Found via a libFuzzer + AddressSanitizer harness around the public
sbc_decode() entry point.

How to trigger — a 14-byte frame is enough to reach the off-by-one:

    9c bd b0 36 7d f2 f2 f2 f2 f2 f2 f2 f2 f2

On an ASan build, sbc_decode() on this input reports:

    ERROR: AddressSanitizer: heap-buffer-overflow  READ of size 1
      #0 sbc_unpack_frame_internal sbc.c:489
      #1 sbc_decode                sbc.c:1225

After the patch the same input is rejected cleanly (sbc_decode returns -1).
Verified on git HEAD b3deb8a ("Release 2.2").

Impact: reachable via any A2DP audio frame (SBC is mandatory for A2DP) and
via libsbc users such as PulseAudio/PipeWire BT modules, GStreamer
sbcparse/sbcdec, and embedded BT audio gateways. The primitive is a 1-byte
out-of-bounds READ; in a long-lived audio pipeline the neighbouring heap is
attacker-shapeable across frames, so the strongest immediate use is a
probabilistic info-leak / heap-layout oracle.
CVSSv3.1 (proposed): AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N (4.3 Medium).

Note: AOSP/Android does not ship libsbc — Android's BT stack uses the OI
codec at bluedroid/embdrv/sbc/, a structurally different implementation not
affected by this bug.

Assigned CVE-2026-16473 (via the Red Hat Security Response Team):
  https://access.redhat.com/security/cve/CVE-2026-16473
  https://bugzilla.redhat.com/show_bug.cgi?id=2503650

A standalone ~60-line ASan reproducer (poc_standalone.c) is available on
request.

 sbc/sbc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sbc/sbc.c b/sbc/sbc.c
index 2fab366..ffe281a 100644
--- a/sbc/sbc.c
+++ b/sbc/sbc.c
@@ -483,7 +483,7 @@ static int sbc_unpack_frame_internal(const uint8_t *data,
 
 				audio_sample = 0;
 				for (bit = 0; bit < bits[ch][sb]; bit++) {
-					if (consumed > len * 8)
+					if (consumed >= len * 8)
 						return -1;
 
 					if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
-- 
2.53.0


[-- Attachment #3: poc_standalone.c --]
[-- Type: application/octet-stream, Size: 2077 bytes --]

/*
 * libsbc (BlueZ SBC codec) — heap-buffer-overflow READ in sbc_unpack_frame_internal
 * Standalone reproducer.
 *
 * Build (Debian/Ubuntu/Fedora — needs libsbc-dev / sbc-devel):
 *   gcc -O0 -g -fsanitize=address poc_standalone.c -lsbc -o poc
 *
 * Or against a freshly built upstream tree:
 *   gcc -O0 -g -fsanitize=address \
 *       -I/path/to/sbc/sbc poc_standalone.c \
 *       /path/to/sbc/sbc/.libs/libsbc.a -lm -o poc
 *
 * Run:
 *   ./poc
 *
 * Expected on a vulnerable build (libsbc <= 2.2 / git HEAD b3deb8a as of
 * 2026-05-02): AddressSanitizer prints
 *   "ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 1 ...
 *    in sbc_unpack_frame_internal sbc.c:489"
 *
 * Expected on a patched build: clean exit (sbc_decode returns -1).
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sbc/sbc.h>

/* 14-byte trigger — first byte 0x9C is the SBC sync word; the remainder is
 * crafted so that the per-sample bit-reader's `consumed` counter reaches
 * exactly len*8 on the final iteration, and the off-by-one bound check at
 * sbc.c:486 (`consumed > len * 8`) lets the next read fall one byte past
 * the input buffer end. */
static const unsigned char TRIGGER[14] = {
    0x9c, 0xbd, 0xb0, 0x36, 0x7d, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
};

int main(void) {
    sbc_t sbc;
    if (sbc_init(&sbc, 0L) < 0) {
        fprintf(stderr, "sbc_init failed\n");
        return 1;
    }

    /* Use a heap allocation so AddressSanitizer's redzone immediately follows
     * the buffer end — that is what makes the 1-byte over-read visible.       */
    unsigned char *in = (unsigned char *)malloc(sizeof TRIGGER);
    if (!in) { sbc_finish(&sbc); return 1; }
    memcpy(in, TRIGGER, sizeof TRIGGER);

    unsigned char out[4096];
    size_t written = 0;
    ssize_t r = sbc_decode(&sbc, in, sizeof TRIGGER, out, sizeof out, &written);

    fprintf(stderr, "sbc_decode returned %zd, written=%zu\n", r, written);

    free(in);
    sbc_finish(&sbc);
    return 0;
}

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-29 21:46 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <1660735014.5089519.1785359719739.ref@mail.yahoo.com>
2026-07-29 21:15 ` [PATCH sbc] sbc: fix 1-byte heap-OOB read in sbc_unpack_frame_internal Oğuzhan Akkaya

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox