* RE: [PATCH 0/8] Add PCIe M.2 Key E connector support for NXP i.MX boards
From: Sherry Sun @ 2026-06-22 3:18 UTC (permalink / raw)
To: Bartosz Golaszewski, Sherry Sun (OSS)
Cc: imx@lists.linux.dev, linux-pci@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org,
linux-pm@vger.kernel.org, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, Frank Li, s.hauer@pengutronix.de,
kernel@pengutronix.de, festevam@gmail.com, Amitkumar Karwar,
Neeraj Sanjay Kale, marcel@holtmann.org, luiz.dentz@gmail.com,
Hongxing Zhu, l.stach@pengutronix.de, lpieralisi@kernel.org,
kwilczynski@kernel.org, mani@kernel.org, bhelgaas@google.com
In-Reply-To: <CAMRc=MfsNa4itdpyGtR16wMb+wMkJwg+9=QJF2-oOoVVfFCF3g@mail.gmail.com>
> On Thu, 18 Jun 2026 12:10:39 +0200, "Sherry Sun (OSS)"
> <sherry.sun@oss.nxp.com> said:
> > From: Sherry Sun <sherry.sun@nxp.com>
> >
> > This series adds support for NXP Wi-Fi/BT combo chips (88W9098, AW693)
> > inserted into PCIe M.2 Key E connectors on several i.MX EVK/MEK boards.
> >
> > For M.2 cards that rely on PCIe L2 link state and wake-up mechanisms,
> > the card must remain powered during suspend. Patch 1 uses the existing
> > dw_pcie_rp::skip_pwrctrl_off flag to skip power-off during suspend and
> > skip power-on during the init path.
> >
> > Alsp the btnxpuart driver is extended to obtain a pwrseq descriptor
> > via the OF graph on the UART controller device in patch 2.
> >
> > Sherry Sun (8):
> > PCI: imx6: Add skip_pwrctrl_off flag support
> > power: sequencing: pcie-m2: Add PCI ID for NXP 88W9098 and AW693
> > Bluetooth
>
> Can this be applied independently without build-time issues?
Hi Bart,
Yes, this patch can be applied independently, I was able to successfully
build it based on the following base-commit:
3ce97bd3c4f18608335e709c24d6a40e7036cab8.
However, please note that it may conflict with the following patch when
applied: https://lore.kernel.org/all/20260617143055.820096-1-wei.deng@oss.qualcomm.com/.
Best Regards
Sherry
>
> > Bluetooth: btnxpuart: Add M.2 Bluetooth device support using pwrseq
> > arm64: dts: imx8mq-evk: Describe the PCIe M.2 Key E connector
> > arm64: dts: imx95-19x19-evk: Describe the PCIe M.2 Key E connector
> > arm64: dts: imx8dxl-evk: Describe the PCIe M.2 Key E connector
> > arm64: dts: imx8qm-mek: Describe the PCIe M.2 Key E connector
> > arm64: dts: imx8qxp-mek: Describe the PCIe M.2 Key E connector
> >
> > arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 56 +++++++++++++-----
> > arch/arm64/boot/dts/freescale/imx8mq-evk.dts | 44 ++++++++++++--
> > arch/arm64/boot/dts/freescale/imx8qm-mek.dts | 58 ++++++++++++++-----
> > arch/arm64/boot/dts/freescale/imx8qxp-mek.dts | 54 ++++++++++++-----
> > .../boot/dts/freescale/imx95-19x19-evk.dts | 55 +++++++++++++-----
> > drivers/bluetooth/btnxpuart.c | 33 ++++++++++-
> > drivers/pci/controller/dwc/pci-imx6.c | 36 +++++++-----
> > drivers/power/sequencing/pwrseq-pcie-m2.c | 4 ++
> > 8 files changed, 264 insertions(+), 76 deletions(-)
> >
> > --
> > 2.50.1
> >
> >
^ permalink raw reply
* RE: src: Remove dead assignments in batocomp
From: bluez.test.bot @ 2026-06-21 19:56 UTC (permalink / raw)
To: linux-bluetooth, kapoorarnav43
In-Reply-To: <20260621181534.92005-1-kapoorarnav43@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 826 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1114380
---Test result---
Test Summary:
CheckPatch PASS 0.87 seconds
GitLint PASS 0.54 seconds
BuildEll PASS 20.26 seconds
BluezMake PASS 655.79 seconds
CheckSmatch PASS 354.56 seconds
bluezmakeextell PASS 182.62 seconds
IncrementalBuild PASS 660.77 seconds
ScanBuild PASS 1029.24 seconds
https://github.com/bluez/bluez/pull/2247
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [bluez/bluez] 7da905: monitor: Remove dead assignments in hwdb helpers
From: Arnav Kapoor @ 2026-06-21 19:07 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1114381
Home: https://github.com/bluez/bluez
Commit: 7da905fd1f43fd80867c319a9a13cd9b9eef6530
https://github.com/bluez/bluez/commit/7da905fd1f43fd80867c319a9a13cd9b9eef6530
Author: Arnav Kapoor <kapoorarnav43@gmail.com>
Date: 2026-06-21 (Sun, 21 Jun 2026)
Changed paths:
M monitor/hwdb.c
Log Message:
-----------
monitor: Remove dead assignments in hwdb helpers
The return values of udev_hwdb_unref() and udev_unref()
are assigned to local variables that are never read again.
Remove the unnecessary assignments.
Found by scan-build.
Signed-by : Arnav Kapoor <kapoorarnav43@gmail.com>
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [bluez/bluez] 64653d: src: Remove dead assignments in batocomp
From: Arnav Kapoor @ 2026-06-21 19:07 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1114380
Home: https://github.com/bluez/bluez
Commit: 64653dc2e8ca4dc20ea5f3b0c22fa2c5979dbf13
https://github.com/bluez/bluez/commit/64653dc2e8ca4dc20ea5f3b0c22fa2c5979dbf13
Author: Arnav Kapoor <kapoorarnav43@gmail.com>
Date: 2026-06-21 (Sun, 21 Jun 2026)
Changed paths:
M src/oui.c
Log Message:
-----------
src: Remove dead assignments in batocomp
The return values of udev_hwdb_unref() and udev_unref()
are assigned to local variables that are never read again.
Remove the unnecessary assignments.
Found by scan-build.
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* RE: Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync()
From: bluez.test.bot @ 2026-06-21 18:28 UTC (permalink / raw)
To: linux-bluetooth, meatuni001
In-Reply-To: <20260621162305.219763-1-meatuni001@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1235 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1114372
---Test result---
Test Summary:
CheckPatch PASS 1.34 seconds
VerifyFixes PASS 0.22 seconds
VerifySignedoff PASS 0.21 seconds
GitLint PASS 0.61 seconds
SubjectPrefix PASS 0.26 seconds
BuildKernel PASS 25.71 seconds
CheckAllWarning PASS 28.24 seconds
CheckSparse PASS 27.09 seconds
BuildKernel32 PASS 24.89 seconds
CheckKernelLLVM SKIP 0.00 seconds
TestRunnerSetup PASS 534.83 seconds
TestRunner_iso-tester PASS 78.14 seconds
IncrementalBuild PASS 24.42 seconds
Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found
https://github.com/bluez/bluetooth-next/pull/336
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [PATCH 2/2] monitor: Remove dead assignments in hwdb helpers
From: Arnav Kapoor @ 2026-06-21 18:27 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Arnav Kapoor
The return values of udev_hwdb_unref() and udev_unref()
are assigned to local variables that are never read again.
Remove the unnecessary assignments.
Found by scan-build.
Signed-by : Arnav Kapoor <kapoorarnav43@gmail.com>
---
monitor/hwdb.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/monitor/hwdb.c b/monitor/hwdb.c
index 6c0d052bb..245b32b0e 100644
--- a/monitor/hwdb.c
+++ b/monitor/hwdb.c
@@ -56,12 +56,12 @@ bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model)
*model = strdup(udev_list_entry_get_value(entry));
}
- hwdb = udev_hwdb_unref(hwdb);
+ udev_hwdb_unref(hwdb);
result = true;
done:
- udev = udev_unref(udev);
+ udev_unref(udev);
return result;
}
@@ -103,12 +103,12 @@ bool hwdb_get_company(const uint8_t *bdaddr, char **company)
}
}
- hwdb = udev_hwdb_unref(hwdb);
+ udev_hwdb_unref(hwdb);
result = true;
done:
- udev = udev_unref(udev);
+ udev_unref(udev);
return result;
}
--
2.53.0
^ permalink raw reply related
* [PATCH] src: Remove dead assignments in batocomp
From: Arnav Kapoor @ 2026-06-21 18:15 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Arnav Kapoor
The return values of udev_hwdb_unref() and udev_unref()
are assigned to local variables that are never read again.
Remove the unnecessary assignments.
Found by scan-build.
---
src/oui.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/oui.c b/src/oui.c
index e20297b93..4d9c3c3e7 100644
--- a/src/oui.c
+++ b/src/oui.c
@@ -47,10 +47,10 @@ char *batocomp(const bdaddr_t *ba)
}
}
- hwdb = udev_hwdb_unref(hwdb);
+ udev_hwdb_unref(hwdb);
done:
- udev = udev_unref(udev);
+ udev_unref(udev);
return comp;
}
--
2.53.0
^ permalink raw reply related
* [PATCH] Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync()
From: Muhammad Bilal @ 2026-06-21 16:23 UTC (permalink / raw)
To: linux-bluetooth
Cc: linux-kernel, Marcel Holtmann, Luiz Augusto von Dentz,
Iulia Tanasescu, Muhammad Bilal, stable
iso_conn_big_sync() drops the socket lock to call hci_get_route() and
then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards
without re-checking that conn is still valid.
While the lock is dropped, the connection can be torn down under the
same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del()
sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also
clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the
lock and reads conn->hcon, conn may be NULL, causing a NULL pointer
dereference (hcon is the first member of struct iso_conn).
This path is reached from iso_sock_recvmsg() for a PA-sync broadcast
sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock
window can race with connection teardown driven by controller events.
Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket
lock and bail out if the connection went away, as already done in the
sibling iso_sock_rebind_bc().
Fixes: 7a17308c17880d ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
---
net/bluetooth/iso.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 3abd8111dda83..7186e8d88c757 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -1589,6 +1589,7 @@ static void iso_conn_big_sync(struct sock *sk)
{
int err;
struct hci_dev *hdev;
+ struct iso_conn *conn;
bdaddr_t src, dst;
u8 src_type;
@@ -1611,8 +1612,17 @@ static void iso_conn_big_sync(struct sock *sk)
hci_dev_lock(hdev);
lock_sock(sk);
+ /* The socket lock was dropped for hci_get_route(), so the connection
+ * may have been torn down meanwhile: iso_chan_del() clears conn and
+ * the broadcast teardown path can clear conn->hcon on its own. Check
+ * both before dereferencing conn->hcon.
+ */
+ conn = iso_pi(sk)->conn;
+ if (!conn || !conn->hcon)
+ goto unlock;
+
if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
- err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
+ err = hci_conn_big_create_sync(hdev, conn->hcon,
&iso_pi(sk)->qos,
iso_pi(sk)->sync_handle,
iso_pi(sk)->bc_num_bis,
@@ -1621,6 +1631,7 @@ static void iso_conn_big_sync(struct sock *sk)
bt_dev_err(hdev, "hci_big_create_sync: %d", err);
}
+unlock:
release_sock(sk);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
--
2.54.0
^ permalink raw reply related
* RE: [BlueZ] unit: test-bap: disable optimization to speed up compilation
From: bluez.test.bot @ 2026-06-21 15:11 UTC (permalink / raw)
To: linux-bluetooth, pav
In-Reply-To: <e0650488e684f464f69cf013d1654f46bb862d46.1782046466.git.pav@iki.fi>
[-- Attachment #1: Type: text/plain, Size: 1909 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1114334
---Test result---
Test Summary:
CheckPatch FAIL 0.57 seconds
GitLint PASS 0.36 seconds
BuildEll PASS 14.54 seconds
BluezMake PASS 454.07 seconds
MakeCheck PASS 17.38 seconds
MakeDistcheck PASS 101.41 seconds
CheckValgrind PASS 144.56 seconds
CheckSmatch PASS 177.29 seconds
bluezmakeextell PASS 60.70 seconds
IncrementalBuild PASS 465.69 seconds
ScanBuild PASS 521.19 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ] unit: test-bap: disable optimization to speed up compilation
WARNING:SPACING: space prohibited between function name and open parenthesis '('
#104: FILE: unit/test-bap.c:18:
+#pragma GCC optimize ("O0")
/github/workspace/src/patch/14638248.patch total: 0 errors, 1 warnings, 12 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14638248.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
https://github.com/bluez/bluez/pull/2246
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [bluez/bluez] d6996f: unit: test-bap: disable optimization to speed up c...
From: Pauli Virtanen @ 2026-06-21 14:37 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1114334
Home: https://github.com/bluez/bluez
Commit: d6996fb9bdcdf329c692eed1461e7515fcd5e05d
https://github.com/bluez/bluez/commit/d6996fb9bdcdf329c692eed1461e7515fcd5e05d
Author: Pauli Virtanen <pav@iki.fi>
Date: 2026-06-21 (Sun, 21 Jun 2026)
Changed paths:
M unit/test-bap.c
Log Message:
-----------
unit: test-bap: disable optimization to speed up compilation
Compilation of this file with optimization takes 12 min with ASAN, 1 min
without. This is too long, and the -O2 doesn't serve purpose for unit
tests.
Disable optimization to reduce to 30 sec with ASAN (5 sec without).
autoconf puts global -O2 in CFLAGS that cannot be overridden eg. with
unit_test_bap_CFLAGS. Use pragma instead.
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [PATCH BlueZ] unit: test-bap: disable optimization to speed up compilation
From: Pauli Virtanen @ 2026-06-21 12:54 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Compilation of this file with optimization takes 12 min with ASAN, 1 min
without. This is too long, and the -O2 doesn't serve purpose for unit
tests.
Disable optimization to reduce to 30 sec with ASAN (5 sec without).
autoconf puts global -O2 in CFLAGS that cannot be overridden eg. with
unit_test_bap_CFLAGS. Use pragma instead.
---
unit/test-bap.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/unit/test-bap.c b/unit/test-bap.c
index 318b405a7..4e89ba6af 100644
--- a/unit/test-bap.c
+++ b/unit/test-bap.c
@@ -13,6 +13,12 @@
#include <config.h>
#endif
+#if defined(__GNUC__)
+/* Speed up compilation */
+#pragma GCC optimize ("O0")
+#undef _FORTIFY_SOURCE
+#endif
+
#define _GNU_SOURCE
#include <unistd.h>
#include <string.h>
--
2.54.0
^ permalink raw reply related
* RTL8852BE (0bda:b853) Bluetooth initialization fails with -16 on ThinkBook 14+ 2026
From: Misha @ 2026-06-21 6:23 UTC (permalink / raw)
To: linux-bluetooth
To: linux-bluetooth@vger.kernel.org
Subject: RTL8852BE (0bda:b853) Bluetooth initialization fails with -16
on ThinkBook 14+ 2026
I'm experiencing Bluetooth initialization failure on a Lenovo ThinkBook
14+ 2026 (Lenovo 14 G8+ AHP - version 2026, released for the Chinese
market) with Realtek RTL8852BE module (USB ID 0bda:b853). The WiFi part
works correctly via rtw89_8852be driver, but Bluetooth fails to
initialize.
Hardware:
- Laptop: Lenovo ThinkBook 14+ 2026 21VR (Chinese market version)
- CPU: AMD Ryzen 7 H 255
- Bluetooth: Realtek RTL8852BE (USB ID 0bda:b853)
- Kernel: 7.1.0-352.vanilla.fc44.x86_64
- Distribution: Fedora 44 Workstation
- linux-firmware: latest from Fedora 44 repos
Problem description:
The Bluetooth device is detected on USB bus (1-5), firmware (see logs
below) are loaded successfully, but initialization fails with
"Opcode 0xfcf0 failed: -16".
Key observations:
1. The USB device ID is 0bda:b853, which appears to be a newer revision of
RTL8852BE not listed in linux-firmware/rtl_bt/ directory
2. Available firmware files: rtl8852au_fw.bin, rtl8852btu_fw.bin,
rtl8852bu_fw.bin,
rtl8852cu_fw.bin - but NO firmware for revision "b853"
3. Driver attempts to load rtl8852bu_fw.bin as fallback, but chip rejects
the vendor command 0xfcf0
Commands output:
➤ lsusb
Bus 001 Device 002: ID 0bda:5411 Realtek Semiconductor Corp. RTS5411 Hub
Bus 001 Device 003: ID 0bda:b853 Realtek Semiconductor Corp.
Bluetooth Radio
➤ lsusb -t
/: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/5p, 480M
|__ Port 002: Dev 002, If 0, Class=Hub, Driver=hub/2p, 480M
|__ Port 005: Dev 003, If 0, Class=Wireless, Driver=btusb, 12M
|__ Port 005: Dev 003, If 1, Class=Wireless, Driver=btusb, 12M
➤ sudo dmesg | grep -i rtw
[ 6.876631] rtw89_8852be 0000:05:00.0: loaded firmware
rtw89/rtw8852b_fw-2.bin
[ 6.876987] rtw89_8852be 0000:05:00.0: enabling device (0000 -> 0003)
[ 6.886529] rtw89_8852be 0000:05:00.0: Firmware version
0.29.29.15 (6fb3ec41), cmd version 0, type 5
[ 6.886547] rtw89_8852be 0000:05:00.0: Firmware version
0.29.29.15 (6fb3ec41), cmd version 0, type 3
[ 7.197141] rtw89_8852be 0000:05:00.0: Firmware element BB
version: 00 28 00 00
[ 7.197185] rtw89_8852be 0000:05:00.0: Firmware element radio A
version: 00 32 00 00
[ 7.197215] rtw89_8852be 0000:05:00.0: Firmware element NCTL
version: 00 0a 00 00
[ 7.197312] rtw89_8852be 0000:05:00.0: Firmware element TXPWR
version: 00 43 00 00
[ 7.197314] rtw89_8852be 0000:05:00.0: Firmware element TXPWR
version: 00 43 00 00
[ 7.197315] rtw89_8852be 0000:05:00.0: Firmware element TXPWR
version: 00 43 00 00
[ 7.197322] rtw89_8852be 0000:05:00.0: Firmware element PWR_TRK
version: 00 32 00 00
[ 7.197330] rtw89_8852be 0000:05:00.0: Firmware element REGD
version: 00 49 00 33
[ 7.197414] rtw89_8852be 0000:05:00.0: chip info CID: 0, CV: 1,
AID: 0, ACV: 1, RFE: 1
[ 7.200038] rtw89_8852be 0000:05:00.0: rfkill hardware state
changed to enable
[ 7.212982] rtw89_8852be 0000:05:00.0 wlp5s0: renamed from wlan0
➤ sudo dmesg | grep -iE 'bluetooth|firmware|mtk|btusb'
[ 0.272488] ACPI: [Firmware Bug]: BIOS _OSI(Linux) query ignored
[ 1.267765] usb 1-5: Product: Bluetooth Radio
[ 4.109424] amdgpu 0000:c6:00.0: [drm] Loading DMUB firmware via
PSP: version=0x08005B00
[ 4.109850] amdgpu 0000:c6:00.0: [VCN instance 0] Found VCN
firmware Version ENC: 1.24 DEC: 9 VEP: 0 Revision: 34
[ 5.075990] SELinux: Permission firmware_load in class system
not defined in policy.
[ 6.030638] systemd[1]: systemd-boot-clear-sysfail.service -
Clear SysFail Entry If The Boot Is Successful skipped, unmet condition
check
ConditionPathExists=/sys/firmware/efi/efivars/LoaderEntrySysFail-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
[ 6.030683] systemd[1]: systemd-hibernate-clear.service - Clear
Stale Hibernate Storage Info skipped, unmet condition check
ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67
[ 6.751552] kvm_amd: [Firmware Bug]: Cannot enable x2AVIC, AVIC
is unsupported
[ 6.876631] rtw89_8852be 0000:05:00.0: loaded firmware
rtw89/rtw8852b_fw-2.bin
[ 6.886529] rtw89_8852be 0000:05:00.0: Firmware version
0.29.29.15 (6fb3ec41), cmd version 0, type 5
[ 6.886547] rtw89_8852be 0000:05:00.0: Firmware version
0.29.29.15 (6fb3ec41), cmd version 0, type 3
[ 7.197141] rtw89_8852be 0000:05:00.0: Firmware element BB
version: 00 28 00 00
[ 7.197185] rtw89_8852be 0000:05:00.0: Firmware element radio A
version: 00 32 00 00
[ 7.197215] rtw89_8852be 0000:05:00.0: Firmware element NCTL
version: 00 0a 00 00
[ 7.197312] rtw89_8852be 0000:05:00.0: Firmware element TXPWR
version: 00 43 00 00
[ 7.197314] rtw89_8852be 0000:05:00.0: Firmware element TXPWR
version: 00 43 00 00
[ 7.197315] rtw89_8852be 0000:05:00.0: Firmware element TXPWR
version: 00 43 00 00
[ 7.197322] rtw89_8852be 0000:05:00.0: Firmware element PWR_TRK
version: 00 32 00 00
[ 7.197330] rtw89_8852be 0000:05:00.0: Firmware element REGD
version: 00 49 00 33
[ 9114.511326] Bluetooth: Core ver 2.22
[ 9114.511401] NET: Registered PF_BLUETOOTH protocol family
[ 9114.511405] Bluetooth: HCI device and connection manager initialized
[ 9114.511415] Bluetooth: HCI socket layer initialized
[ 9114.511420] Bluetooth: L2CAP socket layer initialized
[ 9114.511430] Bluetooth: SCO socket layer initialized
[ 9649.478314] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[ 9649.478328] Bluetooth: BNEP filters: protocol multicast
[ 9649.478340] Bluetooth: BNEP socket layer initialized
[10359.251101] usbcore: registered new interface driver btusb
[10359.255740] Bluetooth: hci0: RTL: examining hci_ver=0b
hci_rev=000b lmp_ver=0b lmp_subver=8852
[10359.258669] Bluetooth: hci0: RTL: rom_version status=0 version=3
[10359.261690] Bluetooth: hci0: RTL: btrtl_initialize: key id 0
[10359.261700] Bluetooth: hci0: RTL: loading rtl_bt/rtl8852bu_fw.bin
[10359.273061] Bluetooth: hci0: RTL: loading rtl_bt/rtl8852bu_config.bin
[10359.279751] Bluetooth: hci0: Opcode 0xfcf0 failed: -16
[10359.282699] Bluetooth: hci0: AOSP extensions version v0.96
[10359.282710] Bluetooth: hci0: AOSP quality report is not supported
➤ rfkill list
0: ideapad_wlan: Wireless LAN
Soft blocked: no
Hard blocked: no
1: ideapad_bluetooth: Bluetooth
Soft blocked: no
Hard blocked: no
2: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
3: hci0: Bluetooth
Soft blocked: no
Hard blocked: no
➤ bluetoothctl show
No default controller available
➤ systemctl status bluetooth
...
Available
...
➤ inxi -Fxz
System:
Kernel: 7.1.0-352.vanilla.fc44.x86_64 arch: x86_64 bits: 64
compiler: gcc
Machine:
Type: Laptop System: LENOVO product: 21VR v: ThinkBook 14 G8+ AHP
Mobo: LENOVO model: LNVNB161216
v: TDCN24WW date: 04/14/2026
CPU:
Info: 8-core model: AMD Ryzen 7 H 255 w/ Radeon 780M Graphics bits: 64
Network:
Device-1: Realtek RTL8111/8168/8211/8411 PCI Express Gigabit Ethernet
vendor: Lenovo driver: r8169 v: kernel port: b000 bus-ID: 03:00.0
IF: enp3s0 state: down mac: <filter>
Device-2: Realtek RTL8852BE PCIe 802.11ax Wireless Network vendor:
Lenovo
driver: rtw89_8852be v: kernel port: a000 bus-ID: 05:00.0
IF: wlp5s0 state: up mac: <filter>
IF-ID-1: docker0 state: down mac: <filter>
Bluetooth:
Device-1: Realtek Bluetooth Radio driver: btusb v: 0.8 type: USB
bus-ID: 1-5:3
Report: hciconfig ID: hci0 rfk-id: 3 state: down
bt-service: enabled,running rfk-block: hardware: no software: no
address: <filter>
Troubleshooting attempts:
1. Performed full power drain - no effect
2. Updated linux-firmware from git.kernel.org - no rtl8852b853 firmware
found
3. Tested on kernels 7.0.x and 7.1.0 - same error
Additional context: Windows 11 is pre-installed on this laptop, WiFi and
Bluetooth works correctly in Windows. Fast Startup is disabled, Secure
Boot is disabled.
Questions:
1. Is USB ID 0bda:b853 a known revision of RTL8852BE that requires specific
firmware mapping in btrtl driver?
2. The driver loads the firmware, but the chip rejects command 0xfcf0.
Does this revision require a different firmware file?
3. Is there a way to force the driver to use a different firmware file for
this USB ID?
4. Should a new entry be added to the btrtl_device_id table in btrtl.c for
USB_DEVICE(0x0bda, 0xb853)?
I'm willing to provide additional logs, test patches, or send the device
for
debugging if needed.
Thank you for your work on the Bluetooth subsystem.
Best regards,
michail383krasnov@mail.ru
^ permalink raw reply
* RE: Bluetooth: L2CAP: validate option length before reading conf opt value
From: bluez.test.bot @ 2026-06-20 21:28 UTC (permalink / raw)
To: linux-bluetooth, meatuni001
In-Reply-To: <20260620195635.41765-1-meatuni001@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1235 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1114248
---Test result---
Test Summary:
CheckPatch PASS 0.84 seconds
VerifyFixes PASS 0.16 seconds
VerifySignedoff PASS 0.16 seconds
GitLint PASS 0.37 seconds
SubjectPrefix PASS 0.16 seconds
BuildKernel PASS 26.86 seconds
CheckAllWarning PASS 29.46 seconds
CheckSparse PASS 28.43 seconds
BuildKernel32 PASS 26.20 seconds
CheckKernelLLVM SKIP 0.00 seconds
TestRunnerSetup PASS 576.66 seconds
TestRunner_l2cap-tester PASS 58.98 seconds
IncrementalBuild PASS 25.33 seconds
Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found
https://github.com/bluez/bluetooth-next/pull/335
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: [BlueZ,1/2] shared: harden btsnoop trace parsing
From: bluez.test.bot @ 2026-06-20 21:09 UTC (permalink / raw)
To: linux-bluetooth, geraldonetto
In-Reply-To: <20260620192228.2692610-1-geraldonetto@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 990 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1114243
---Test result---
Test Summary:
CheckPatch PASS 1.68 seconds
GitLint PASS 0.51 seconds
BuildEll PASS 20.34 seconds
BluezMake PASS 667.04 seconds
MakeCheck PASS 18.36 seconds
MakeDistcheck PASS 251.81 seconds
CheckValgrind PASS 303.17 seconds
CheckSmatch PASS 354.29 seconds
bluezmakeextell PASS 184.13 seconds
IncrementalBuild PASS 699.32 seconds
ScanBuild PASS 1041.22 seconds
https://github.com/bluez/bluez/pull/2245
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: audio: harden A2DP parser handling
From: bluez.test.bot @ 2026-06-20 21:07 UTC (permalink / raw)
To: linux-bluetooth, geraldonetto
In-Reply-To: <20260620191735.2675946-2-geraldonetto@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 990 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1114241
---Test result---
Test Summary:
CheckPatch PASS 1.10 seconds
GitLint PASS 0.66 seconds
BuildEll PASS 20.28 seconds
BluezMake PASS 655.58 seconds
MakeCheck PASS 18.46 seconds
MakeDistcheck PASS 246.91 seconds
CheckValgrind PASS 297.01 seconds
CheckSmatch PASS 350.72 seconds
bluezmakeextell PASS 181.74 seconds
IncrementalBuild PASS 662.39 seconds
ScanBuild PASS 1035.90 seconds
https://github.com/bluez/bluez/pull/2244
---
Regards,
Linux Bluetooth
^ permalink raw reply
* [bluez/bluez] cfc7fc: shared: harden btsnoop trace parsing
From: Geraldo Netto @ 2026-06-20 20:09 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1114243
Home: https://github.com/bluez/bluez
Commit: cfc7fc7c0b3c95c9445764d5a2d21323e803c32d
https://github.com/bluez/bluez/commit/cfc7fc7c0b3c95c9445764d5a2d21323e803c32d
Author: Geraldo Netto <geraldonetto@gmail.com>
Date: 2026-06-20 (Sat, 20 Jun 2026)
Changed paths:
M Makefile.am
M monitor/analyze.c
M monitor/control.c
M src/shared/btsnoop.c
M src/shared/btsnoop.h
A unit/test-btsnoop.c
A unit/test-btsnoop.h
Log Message:
-----------
shared: harden btsnoop trace parsing
Commit: ffadedbfd700d2cc08c39a6a0ffddba0184a72b0
https://github.com/bluez/bluez/commit/ffadedbfd700d2cc08c39a6a0ffddba0184a72b0
Author: Geraldo Netto <geraldonetto@gmail.com>
Date: 2026-06-20 (Sat, 20 Jun 2026)
Changed paths:
M Makefile.am
A unit/test-btsnoop-pklg.c
M unit/test-btsnoop.c
Log Message:
-----------
unit: split btsnoop pklg tests
Compare: https://github.com/bluez/bluez/compare/cfc7fc7c0b3c%5E...ffadedbfd700
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [bluez/bluez] 92081f: audio: harden a2dp parsers
From: Geraldo Netto @ 2026-06-20 20:09 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1114241
Home: https://github.com/bluez/bluez
Commit: 92081f8ff1f50f6acd338bea9222e9bebe6e319c
https://github.com/bluez/bluez/commit/92081f8ff1f50f6acd338bea9222e9bebe6e319c
Author: Geraldo Netto <geraldonetto@gmail.com>
Date: 2026-06-20 (Sat, 20 Jun 2026)
Changed paths:
M Makefile.am
M Makefile.plugins
A profiles/audio/a2dp-helpers.c
A profiles/audio/a2dp-helpers.h
M profiles/audio/a2dp.c
A unit/test-a2dp.c
Log Message:
-----------
audio: harden a2dp parsers
Commit: dc20a9c9a09e4dd566a0bfa5edde8be179545880
https://github.com/bluez/bluez/commit/dc20a9c9a09e4dd566a0bfa5edde8be179545880
Author: Geraldo Netto <geraldonetto@gmail.com>
Date: 2026-06-20 (Sat, 20 Jun 2026)
Changed paths:
M profiles/audio/a2dp-helpers.c
Log Message:
-----------
audio: reduce a2dp parser complexity
Compare: https://github.com/bluez/bluez/compare/92081f8ff1f5%5E...dc20a9c9a09e
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [PATCH] Bluetooth: L2CAP: validate option length before reading conf opt value
From: Muhammad Bilal @ 2026-06-20 19:56 UTC (permalink / raw)
To: linux-bluetooth
Cc: linux-kernel, Marcel Holtmann, Luiz Augusto von Dentz,
Muhammad Bilal, stable
l2cap_get_conf_opt() derives the option length from the
attacker-controlled opt->len field and immediately dereferences
opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a
raw pointer for the default case) before any caller has confirmed
that opt->len bytes are present in the buffer. The callers
(l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and
l2cap_conf_rfc_get()) only detect a malformed option afterwards, once
the running length has gone negative, by which point the
out-of-bounds read has already executed.
An existing post-hoc length check keeps the garbage value from being
consumed, so this is not a data leak in the current control flow. It
is still a validate-after-use ordering bug: up to 4 bytes are read
past the end of the buffer before it is known to contain them, and it
is fragile to future changes in the callers.
Fix it at the source. Pass the end of the buffer into
l2cap_get_conf_opt() and refuse to touch opt->val unless the full
option (header + value) fits. Each caller computes an end pointer
once before the loop and checks the return value directly instead of
inferring the error from a negative length.
Fixes: 7c9cbd0b5e38 ("Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
---
net/bluetooth/l2cap_core.c | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c4ccfbda9d789..ebe44990a22e2 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3052,13 +3052,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
return NULL;
}
-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
- unsigned long *val)
+static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type,
+ int *olen, unsigned long *val)
{
struct l2cap_conf_opt *opt = *ptr;
int len;
+ /* opt->len is attacker-controlled. Validate that the full option
+ * (header + value) actually fits in the buffer before touching
+ * opt->val, otherwise the switch below reads past the end of the
+ * caller's buffer.
+ */
+ if (end - *ptr < L2CAP_CONF_OPT_SIZE)
+ return -EINVAL;
+
len = L2CAP_CONF_OPT_SIZE + opt->len;
+ if (end - *ptr < len)
+ return -EINVAL;
+
*ptr += len;
*type = opt->type;
@@ -3426,6 +3437,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
void *ptr = rsp->data;
void *endptr = data + data_size;
void *req = chan->conf_req;
+ void *req_end = req + chan->conf_len;
int len = chan->conf_len;
int type, hint, olen;
unsigned long val;
@@ -3439,9 +3451,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
BT_DBG("chan %p", chan);
while (len >= L2CAP_CONF_OPT_SIZE) {
- len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
- if (len < 0)
+ int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val);
+
+ if (ret < 0)
break;
+ len -= ret;
hint = type & L2CAP_CONF_HINT;
type &= L2CAP_CONF_MASK;
@@ -3669,6 +3683,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
struct l2cap_conf_req *req = data;
void *ptr = req->data;
void *endptr = data + size;
+ void *rsp_end = rsp + len;
int type, olen;
unsigned long val;
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
@@ -3677,9 +3692,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
while (len >= L2CAP_CONF_OPT_SIZE) {
- len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
- if (len < 0)
+ int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
+
+ if (ret < 0)
break;
+ len -= ret;
switch (type) {
case L2CAP_CONF_MTU:
@@ -3930,6 +3947,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
{
int type, olen;
unsigned long val;
+ void *rsp_end = rsp + len;
/* Use sane default values in case a misbehaving remote device
* did not send an RFC or extended window size option.
*/
@@ -3948,9 +3966,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
return;
while (len >= L2CAP_CONF_OPT_SIZE) {
- len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
- if (len < 0)
+ int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
+
+ if (ret < 0)
break;
+ len -= ret;
switch (type) {
case L2CAP_CONF_RFC:
--
2.54.0
^ permalink raw reply related
* [PATCH BlueZ 2/2] unit: split btsnoop pklg tests
From: Geraldo Netto @ 2026-06-20 19:22 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Geraldo Netto
In-Reply-To: <20260620192228.2692610-1-geraldonetto@gmail.com>
---
Makefile.am | 3 +-
unit/test-btsnoop-pklg.c | 266 ++++++++++++++++++++++
unit/test-btsnoop.c | 462 +++++++++------------------------------
3 files changed, 374 insertions(+), 357 deletions(-)
create mode 100644 unit/test-btsnoop-pklg.c
diff --git a/Makefile.am b/Makefile.am
index 4887934a9..db63f3b07 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -589,7 +589,8 @@ unit_test_textfile_LDADD = src/libshared-glib.la $(GLIB_LIBS)
unit_tests += unit/test-btsnoop
-unit_test_btsnoop_SOURCES = unit/test-btsnoop.c \
+unit_test_btsnoop_SOURCES = unit/test-btsnoop.c unit/test-btsnoop.h \
+ unit/test-btsnoop-pklg.c \
src/shared/btsnoop.h src/shared/btsnoop.c
unit_test_btsnoop_LDADD = src/libshared-glib.la $(GLIB_LIBS)
diff --git a/unit/test-btsnoop-pklg.c b/unit/test-btsnoop-pklg.c
new file mode 100644
index 000000000..ff3c24354
--- /dev/null
+++ b/unit/test-btsnoop-pklg.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "src/shared/att-types.h"
+#include "src/shared/btsnoop.h"
+#include "unit/test-btsnoop.h"
+
+#define PKLG_PAYLOAD_OFFSET 9
+
+struct test_pklg_pkt {
+ uint32_t len;
+ uint64_t ts;
+ uint8_t type;
+} __packed;
+
+struct read_result {
+ uint8_t data[BTSNOOP_MAX_PACKET_SIZE];
+ uint16_t size;
+ uint16_t index;
+ uint16_t opcode;
+ struct timeval tv;
+};
+
+static void read_result_init(struct read_result *result)
+{
+ memset(result->data, 0xa5, sizeof(result->data));
+ result->size = 0;
+ result->index = 0xffff;
+ result->opcode = 0xffff;
+ memset(&result->tv, 0, sizeof(result->tv));
+}
+
+static void append_bytes(GByteArray *array, const void *data, size_t size)
+{
+ if (size)
+ g_byte_array_append(array, data, size);
+}
+
+static void append_pklg_packet(GByteArray *array, bool little_endian,
+ uint32_t payload_len, uint64_t ts,
+ uint8_t type, const void *data, size_t data_len)
+{
+ struct test_pklg_pkt pkt;
+ uint32_t len = PKLG_PAYLOAD_OFFSET + payload_len;
+
+ pkt.len = little_endian ? htole32(len) : htobe32(len);
+ pkt.ts = little_endian ? htole64(ts) : htobe64(ts);
+ pkt.type = type;
+
+ append_bytes(array, &pkt, sizeof(pkt));
+ append_bytes(array, data, data_len);
+}
+
+static char *write_tmp_trace(const void *data, size_t size)
+{
+ char *path = NULL;
+ ssize_t written;
+ int fd;
+
+ fd = g_file_open_tmp("bluez-btsnoop-XXXXXX", &path, NULL);
+ g_assert(fd >= 0);
+ written = write(fd, data, size);
+ g_assert_cmpint(written, ==, (ssize_t) size);
+ g_assert_cmpint(close(fd), ==, 0);
+
+ return path;
+}
+
+static bool read_tmp_trace(const void *trace, size_t trace_len,
+ uint16_t data_size, struct read_result *result)
+{
+ struct btsnoop *btsnoop;
+ char *path;
+ bool ok;
+
+ read_result_init(result);
+ path = write_tmp_trace(trace, trace_len);
+ btsnoop = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+ unlink(path);
+ g_free(path);
+
+ if (!btsnoop)
+ return false;
+
+ ok = btsnoop_read_hci(btsnoop, &result->tv, &result->index,
+ &result->opcode, result->data, data_size,
+ &result->size);
+ btsnoop_unref(btsnoop);
+
+ return ok;
+}
+
+static void test_pklg_big_endian_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x0e, 0x01, 0x00 };
+ struct read_result result;
+
+ append_pklg_packet(trace, false, sizeof(payload),
+ ((uint64_t) 123 << 32) | 456, 0x01, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len, sizeof(payload),
+ &result));
+ g_assert_cmpint(result.index, ==, 0);
+ g_assert_cmpint(result.opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
+ g_assert_cmpint(result.size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(result.data, payload, sizeof(payload)), ==, 0);
+ g_assert_cmpint(result.tv.tv_sec, ==, 123);
+ g_assert_cmpint(result.tv.tv_usec, ==, 456);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_little_endian_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ struct read_result result;
+
+ append_pklg_packet(trace, true, sizeof(payload),
+ ((uint64_t) 456 << 32) | 123, 0x00, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len, sizeof(payload),
+ &result));
+ g_assert_cmpint(result.index, ==, 0);
+ g_assert_cmpint(result.opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
+ g_assert_cmpint(result.size, ==, sizeof(payload));
+ g_assert_cmpint(result.data[0], ==, payload[0]);
+ g_assert_cmpint(result.tv.tv_sec, ==, 123);
+ g_assert_cmpint(result.tv.tv_usec, ==, 456);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_rejects_short_length(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ struct test_pklg_pkt pkt;
+ const uint8_t padding[] = { 0x00, 0x00, 0x00 };
+ struct read_result result;
+
+ pkt.len = htobe32(PKLG_PAYLOAD_OFFSET - 1);
+ pkt.ts = 0;
+ pkt.type = 0x01;
+
+ append_bytes(trace, &pkt, sizeof(pkt));
+ append_bytes(trace, padding, sizeof(padding));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, &result));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_rejects_small_capacity(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ struct read_result result;
+
+ append_pklg_packet(trace, false, sizeof(payload), 0, 0x01, payload,
+ sizeof(payload));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 2, &result));
+ g_assert_cmpint(result.data[0], ==, 0xa5);
+ g_assert_cmpint(result.data[1], ==, 0xa5);
+ g_assert_cmpint(result.data[2], ==, 0xa5);
+ g_assert_cmpint(result.data[3], ==, 0xa5);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_rejects_short_payload(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ struct read_result result;
+
+ append_pklg_packet(trace, false, 4, 0, 0x01, payload,
+ sizeof(payload));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 4, &result));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_type_map(void)
+{
+ static const struct {
+ uint8_t type;
+ uint16_t index;
+ uint16_t opcode;
+ } cases[] = {
+ { 0x02, 0x0000, BTSNOOP_OPCODE_ACL_TX_PKT },
+ { 0x03, 0x0000, BTSNOOP_OPCODE_ACL_RX_PKT },
+ { 0x08, 0x0000, BTSNOOP_OPCODE_SCO_TX_PKT },
+ { 0x09, 0x0000, BTSNOOP_OPCODE_SCO_RX_PKT },
+ { 0x12, 0x0000, BTSNOOP_OPCODE_ISO_TX_PKT },
+ { 0x13, 0x0000, BTSNOOP_OPCODE_ISO_RX_PKT },
+ { 0x0b, 0x0000, BTSNOOP_OPCODE_VENDOR_DIAG },
+ { 0xfc, 0xffff, BTSNOOP_OPCODE_SYSTEM_NOTE },
+ { 0xaa, 0xffff, 0xffff },
+ };
+ const uint8_t payload[] = { 0x00, 0x01, 0x02 };
+ unsigned int i;
+
+ for (i = 0; i < G_N_ELEMENTS(cases); i++) {
+ GByteArray *trace = g_byte_array_new();
+ struct read_result result;
+
+ append_pklg_packet(trace, false, sizeof(payload), 0,
+ cases[i].type, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len,
+ sizeof(payload), &result));
+ g_assert_cmpint(result.index, ==, cases[i].index);
+ g_assert_cmpint(result.opcode, ==, cases[i].opcode);
+ g_byte_array_unref(trace);
+ }
+}
+
+static void test_pklg_truncation_fuzz(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ size_t len;
+
+ append_pklg_packet(trace, false, sizeof(payload), 0, 0x01, payload,
+ sizeof(payload));
+
+ for (len = 0; len < trace->len; len++) {
+ struct read_result result;
+
+ g_assert_false(read_tmp_trace(trace->data, len, sizeof(payload),
+ &result));
+ }
+
+ g_byte_array_unref(trace);
+}
+
+void add_pklg_tests(void)
+{
+ g_test_add_func("/pklg/big-endian/valid", test_pklg_big_endian_valid);
+ g_test_add_func("/pklg/little-endian/valid",
+ test_pklg_little_endian_valid);
+ g_test_add_func("/pklg/length/short", test_pklg_rejects_short_length);
+ g_test_add_func("/pklg/capacity/reject",
+ test_pklg_rejects_small_capacity);
+ g_test_add_func("/pklg/payload/short", test_pklg_rejects_short_payload);
+ g_test_add_func("/pklg/type-map", test_pklg_type_map);
+ g_test_add_func("/pklg/fuzz/truncation", test_pklg_truncation_fuzz);
+}
diff --git a/unit/test-btsnoop.c b/unit/test-btsnoop.c
index 710209097..fa7587d1a 100644
--- a/unit/test-btsnoop.c
+++ b/unit/test-btsnoop.c
@@ -18,7 +18,6 @@
#include "unit/test-btsnoop.h"
#define BTSNOOP_EPOCH_OFFSET 0x00E03AB44A676000ull
-#define PKLG_PAYLOAD_OFFSET 9
struct test_btsnoop_hdr {
uint8_t id[8];
@@ -34,22 +33,31 @@ struct test_btsnoop_pkt {
uint64_t ts;
} __packed;
-struct test_pklg_pkt {
- uint32_t len;
- uint64_t ts;
- uint8_t type;
-} __packed;
+struct read_result {
+ uint8_t data[BTSNOOP_MAX_PACKET_SIZE];
+ uint16_t size;
+ uint16_t index;
+ uint16_t opcode;
+ struct timeval tv;
+};
static const uint8_t btsnoop_id[] = {
0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00
};
-static void append_bytes(GByteArray *array, const void *data, size_t size)
+static void read_result_init(struct read_result *result)
{
- if (!size)
- return;
+ memset(result->data, 0xa5, sizeof(result->data));
+ result->size = 0;
+ result->index = 0xffff;
+ result->opcode = 0xffff;
+ memset(&result->tv, 0, sizeof(result->tv));
+}
- g_byte_array_append(array, data, size);
+static void append_bytes(GByteArray *array, const void *data, size_t size)
+{
+ if (size)
+ g_byte_array_append(array, data, size);
}
static void append_btsnoop_header(GByteArray *array, uint32_t format)
@@ -79,21 +87,6 @@ static void append_btsnoop_packet(GByteArray *array, uint32_t len,
append_bytes(array, data, data_len);
}
-static void append_pklg_packet(GByteArray *array, bool little_endian,
- uint32_t payload_len, uint64_t ts,
- uint8_t type, const void *data, size_t data_len)
-{
- struct test_pklg_pkt pkt;
- uint32_t len = PKLG_PAYLOAD_OFFSET + payload_len;
-
- pkt.len = little_endian ? htole32(len) : htobe32(len);
- pkt.ts = little_endian ? htole64(ts) : htobe64(ts);
- pkt.type = type;
-
- append_bytes(array, &pkt, sizeof(pkt));
- append_bytes(array, data, data_len);
-}
-
static char *write_tmp_trace(const void *data, size_t size)
{
char *path = NULL;
@@ -127,9 +120,8 @@ static void unlink_rotated(const char *path, unsigned int count)
unsigned int i;
for (i = 0; i <= count; i++) {
- char *name;
+ char *name = g_strdup_printf("%s.%u", path, i);
- name = g_strdup_printf("%s.%u", path, i);
unlink(name);
g_free(name);
}
@@ -137,14 +129,13 @@ static void unlink_rotated(const char *path, unsigned int count)
static bool read_tmp_trace(const void *trace, size_t trace_len,
unsigned long flags, uint16_t data_size,
- uint8_t *data, uint16_t *size,
- uint16_t *index, uint16_t *opcode,
- struct timeval *tv)
+ struct read_result *result)
{
struct btsnoop *btsnoop;
char *path;
- bool result;
+ bool ok;
+ read_result_init(result);
path = write_tmp_trace(trace, trace_len);
btsnoop = btsnoop_open(path, flags);
unlink(path);
@@ -153,54 +144,51 @@ static bool read_tmp_trace(const void *trace, size_t trace_len,
if (!btsnoop)
return false;
- result = btsnoop_read_hci(btsnoop, tv, index, opcode, data,
- data_size, size);
+ ok = btsnoop_read_hci(btsnoop, &result->tv, &result->index,
+ &result->opcode, result->data, data_size,
+ &result->size);
btsnoop_unref(btsnoop);
- return result;
+ return ok;
}
static bool read_trace_file(const char *path, unsigned long flags,
- uint8_t *data, uint16_t data_size,
- uint16_t *size, uint16_t *index,
- uint16_t *opcode, struct timeval *tv)
+ uint16_t data_size, struct read_result *result)
{
struct btsnoop *btsnoop;
- bool result;
+ bool ok;
+ read_result_init(result);
btsnoop = btsnoop_open(path, flags);
g_assert_nonnull(btsnoop);
- result = btsnoop_read_hci(btsnoop, tv, index, opcode, data,
- data_size, size);
+ ok = btsnoop_read_hci(btsnoop, &result->tv, &result->index,
+ &result->opcode, result->data, data_size,
+ &result->size);
btsnoop_unref(btsnoop);
- return result;
+ return ok;
}
static void test_btsnoop_hci_valid(void)
{
GByteArray *trace = g_byte_array_new();
const uint8_t payload[] = { 0x01, 0x02, 0x03 };
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0xffff;
- uint16_t opcode = 0xffff;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
append_btsnoop_packet(trace, sizeof(payload), 0x02,
BTSNOOP_EPOCH_OFFSET + 1234567, payload,
sizeof(payload));
- g_assert_true(read_tmp_trace(trace->data, trace->len, 0, sizeof(data),
- data, &size, &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 0);
- g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
- g_assert_cmpint(size, ==, sizeof(payload));
- g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
- g_assert_cmpint(tv.tv_sec, ==, 946684801);
- g_assert_cmpint(tv.tv_usec, ==, 234567);
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0,
+ sizeof(payload), &result));
+ g_assert_cmpint(result.index, ==, 0);
+ g_assert_cmpint(result.opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
+ g_assert_cmpint(result.size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(result.data, payload, sizeof(payload)), ==, 0);
+ g_assert_cmpint(result.tv.tv_sec, ==, 946684801);
+ g_assert_cmpint(result.tv.tv_usec, ==, 234567);
g_byte_array_unref(trace);
}
@@ -247,12 +235,9 @@ static void test_btsnoop_write_hci_roundtrip(void)
{
const uint8_t command[] = { 0x01, 0x02, 0x03 };
const uint8_t event[] = { 0x04, 0x05 };
- uint8_t data[sizeof(command)];
struct btsnoop *btsnoop;
+ struct read_result result;
struct timeval tv = { .tv_sec = 946684802, .tv_usec = 345678 };
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
char *path = new_tmp_path();
btsnoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_HCI);
@@ -272,24 +257,30 @@ static void test_btsnoop_write_hci_roundtrip(void)
command, sizeof(command)));
btsnoop_unref(btsnoop);
- g_assert_true(read_trace_file(path, 0, data, sizeof(data), &size,
- &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 0);
- g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
- g_assert_cmpint(size, ==, sizeof(command));
- g_assert_cmpint(memcmp(data, command, sizeof(command)), ==, 0);
+ g_assert_true(read_trace_file(path, 0, sizeof(command), &result));
+ g_assert_cmpint(result.index, ==, 0);
+ g_assert_cmpint(result.opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
+ g_assert_cmpint(result.size, ==, sizeof(command));
+ g_assert_cmpint(memcmp(result.data, command, sizeof(command)), ==, 0);
btsnoop = btsnoop_open(path, 0);
g_assert_nonnull(btsnoop);
- g_assert_true(btsnoop_read_hci(btsnoop, &tv, &index, &opcode, data,
- sizeof(data), &size));
- g_assert_true(btsnoop_read_hci(btsnoop, &tv, &index, &opcode, data,
- sizeof(data), &size));
- g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
- g_assert_cmpint(size, ==, sizeof(event));
- g_assert_cmpint(memcmp(data, event, sizeof(event)), ==, 0);
- g_assert_false(btsnoop_read_hci(btsnoop, &tv, &index, &opcode, data,
- sizeof(data), &size));
+ read_result_init(&result);
+ g_assert_true(btsnoop_read_hci(btsnoop, &result.tv,
+ &result.index, &result.opcode,
+ result.data, sizeof(result.data),
+ &result.size));
+ g_assert_true(btsnoop_read_hci(btsnoop, &result.tv,
+ &result.index, &result.opcode,
+ result.data, sizeof(result.data),
+ &result.size));
+ g_assert_cmpint(result.opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
+ g_assert_cmpint(result.size, ==, sizeof(event));
+ g_assert_cmpint(memcmp(result.data, event, sizeof(event)), ==, 0);
+ g_assert_false(btsnoop_read_hci(btsnoop, &result.tv,
+ &result.index, &result.opcode,
+ result.data, sizeof(result.data),
+ &result.size));
btsnoop_unref(btsnoop);
unlink(path);
@@ -299,12 +290,9 @@ static void test_btsnoop_write_hci_roundtrip(void)
static void test_btsnoop_write_monitor_roundtrip(void)
{
const uint8_t payload[] = { 0xaa, 0xbb };
- uint8_t data[sizeof(payload)];
struct btsnoop *btsnoop;
+ struct read_result result;
struct timeval tv = { .tv_sec = 946684800, .tv_usec = 0 };
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
char *path = new_tmp_path();
btsnoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_MONITOR);
@@ -313,12 +301,11 @@ static void test_btsnoop_write_monitor_roundtrip(void)
payload, sizeof(payload)));
btsnoop_unref(btsnoop);
- g_assert_true(read_trace_file(path, 0, data, sizeof(data), &size,
- &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 7);
- g_assert_cmpint(opcode, ==, 0x1234);
- g_assert_cmpint(size, ==, sizeof(payload));
- g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
+ g_assert_true(read_trace_file(path, 0, sizeof(payload), &result));
+ g_assert_cmpint(result.index, ==, 7);
+ g_assert_cmpint(result.opcode, ==, 0x1234);
+ g_assert_cmpint(result.size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(result.data, payload, sizeof(payload)), ==, 0);
unlink(path);
g_free(path);
@@ -355,22 +342,18 @@ static void test_btsnoop_monitor_valid(void)
{
GByteArray *trace = g_byte_array_new();
const uint8_t payload[] = { 0xaa, 0xbb };
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0xffff;
- uint16_t opcode = 0xffff;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_MONITOR);
append_btsnoop_packet(trace, sizeof(payload), 0x00051234,
BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
- g_assert_true(read_tmp_trace(trace->data, trace->len, 0, sizeof(data),
- data, &size, &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 5);
- g_assert_cmpint(opcode, ==, 0x1234);
- g_assert_cmpint(size, ==, sizeof(payload));
- g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0,
+ sizeof(payload), &result));
+ g_assert_cmpint(result.index, ==, 5);
+ g_assert_cmpint(result.opcode, ==, 0x1234);
+ g_assert_cmpint(result.size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(result.data, payload, sizeof(payload)), ==, 0);
g_byte_array_unref(trace);
}
@@ -379,22 +362,18 @@ static void test_btsnoop_uart_valid(void)
{
GByteArray *trace = g_byte_array_new();
const uint8_t payload[] = { 0x04, 0x0e, 0x01, 0x00 };
- uint8_t data[sizeof(payload) - 1];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0xffff;
- uint16_t opcode = 0xffff;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
append_btsnoop_packet(trace, sizeof(payload), 0,
BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
- g_assert_true(read_tmp_trace(trace->data, trace->len, 0, sizeof(data),
- data, &size, &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 0);
- g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
- g_assert_cmpint(size, ==, sizeof(payload) - 1);
- g_assert_cmpint(memcmp(data, payload + 1, sizeof(data)), ==, 0);
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0,
+ sizeof(payload) - 1, &result));
+ g_assert_cmpint(result.index, ==, 0);
+ g_assert_cmpint(result.opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
+ g_assert_cmpint(result.size, ==, sizeof(payload) - 1);
+ g_assert_cmpint(memcmp(result.data, payload + 1, result.size), ==, 0);
g_byte_array_unref(trace);
}
@@ -420,20 +399,15 @@ static void test_btsnoop_uart_opcode_map(void)
for (i = 0; i < G_N_ELEMENTS(cases); i++) {
GByteArray *trace = g_byte_array_new();
const uint8_t payload[] = { cases[i].type, 0x00 };
- uint8_t data[1];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
append_btsnoop_packet(trace, sizeof(payload), cases[i].flags,
BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
- g_assert_true(read_tmp_trace(trace->data, trace->len, 0,
- sizeof(data), data, &size,
- &index, &opcode, &tv));
- g_assert_cmpint(opcode, ==, cases[i].opcode);
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0, 1,
+ &result));
+ g_assert_cmpint(result.opcode, ==, cases[i].opcode);
g_byte_array_unref(trace);
}
}
@@ -442,22 +416,17 @@ static void test_btsnoop_rejects_small_capacity(void)
{
GByteArray *trace = g_byte_array_new();
const uint8_t payload[] = { 0x01, 0x02, 0x03 };
- uint8_t data[4] = { 0xa5, 0xa5, 0xa5, 0xa5 };
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
append_btsnoop_packet(trace, sizeof(payload), 0x02,
BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
- g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 2, data,
- &size, &index, &opcode, &tv));
- g_assert_cmpint(data[0], ==, 0xa5);
- g_assert_cmpint(data[1], ==, 0xa5);
- g_assert_cmpint(data[2], ==, 0xa5);
- g_assert_cmpint(data[3], ==, 0xa5);
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 2, &result));
+ g_assert_cmpint(result.data[0], ==, 0xa5);
+ g_assert_cmpint(result.data[1], ==, 0xa5);
+ g_assert_cmpint(result.data[2], ==, 0xa5);
+ g_assert_cmpint(result.data[3], ==, 0xa5);
g_byte_array_unref(trace);
}
@@ -465,17 +434,13 @@ static void test_btsnoop_rejects_small_capacity(void)
static void test_btsnoop_rejects_timestamp_underflow(void)
{
GByteArray *trace = g_byte_array_new();
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
append_btsnoop_packet(trace, 0, 0x02, BTSNOOP_EPOCH_OFFSET - 1,
NULL, 0);
- g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, NULL,
- &size, &index, &opcode, &tv));
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, &result));
g_byte_array_unref(trace);
}
@@ -483,16 +448,12 @@ static void test_btsnoop_rejects_timestamp_underflow(void)
static void test_btsnoop_rejects_uart_zero_length(void)
{
GByteArray *trace = g_byte_array_new();
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
append_btsnoop_packet(trace, 0, 0, BTSNOOP_EPOCH_OFFSET, NULL, 0);
- g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, NULL,
- &size, &index, &opcode, &tv));
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, &result));
g_byte_array_unref(trace);
}
@@ -500,16 +461,12 @@ static void test_btsnoop_rejects_uart_zero_length(void)
static void test_btsnoop_rejects_uart_short_type(void)
{
GByteArray *trace = g_byte_array_new();
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
append_btsnoop_packet(trace, 1, 0, BTSNOOP_EPOCH_OFFSET, NULL, 0);
- g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, NULL,
- &size, &index, &opcode, &tv));
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, &result));
g_byte_array_unref(trace);
}
@@ -518,187 +475,17 @@ static void test_btsnoop_rejects_short_payload(void)
{
GByteArray *trace = g_byte_array_new();
const uint8_t payload[] = { 0x01, 0x02 };
- uint8_t data[3];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
append_btsnoop_packet(trace, 3, 0x02, BTSNOOP_EPOCH_OFFSET,
payload, sizeof(payload));
- g_assert_false(read_tmp_trace(trace->data, trace->len, 0,
- sizeof(data), data, &size,
- &index, &opcode, &tv));
-
- g_byte_array_unref(trace);
-}
-
-static void test_pklg_big_endian_valid(void)
-{
- GByteArray *trace = g_byte_array_new();
- const uint8_t payload[] = { 0x0e, 0x01, 0x00 };
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0xffff;
- uint16_t opcode = 0xffff;
-
- append_pklg_packet(trace, false, sizeof(payload),
- ((uint64_t) 123 << 32) | 456, 0x01, payload,
- sizeof(payload));
-
- g_assert_true(read_tmp_trace(trace->data, trace->len,
- BTSNOOP_FLAG_PKLG_SUPPORT, sizeof(data),
- data, &size, &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 0);
- g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
- g_assert_cmpint(size, ==, sizeof(payload));
- g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
- g_assert_cmpint(tv.tv_sec, ==, 123);
- g_assert_cmpint(tv.tv_usec, ==, 456);
-
- g_byte_array_unref(trace);
-}
-
-static void test_pklg_little_endian_valid(void)
-{
- GByteArray *trace = g_byte_array_new();
- const uint8_t payload[] = { 0x01, 0x02, 0x03 };
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0xffff;
- uint16_t opcode = 0xffff;
-
- append_pklg_packet(trace, true, sizeof(payload),
- ((uint64_t) 456 << 32) | 123, 0x00, payload,
- sizeof(payload));
-
- g_assert_true(read_tmp_trace(trace->data, trace->len,
- BTSNOOP_FLAG_PKLG_SUPPORT, sizeof(data),
- data, &size, &index, &opcode, &tv));
- g_assert_cmpint(index, ==, 0);
- g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
- g_assert_cmpint(size, ==, sizeof(payload));
- g_assert_cmpint(data[0], ==, payload[0]);
- g_assert_cmpint(tv.tv_sec, ==, 123);
- g_assert_cmpint(tv.tv_usec, ==, 456);
-
- g_byte_array_unref(trace);
-}
-
-static void test_pklg_rejects_short_length(void)
-{
- GByteArray *trace = g_byte_array_new();
- struct test_pklg_pkt pkt;
- const uint8_t padding[] = { 0x00, 0x00, 0x00 };
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
-
- pkt.len = htobe32(PKLG_PAYLOAD_OFFSET - 1);
- pkt.ts = 0;
- pkt.type = 0x01;
-
- append_bytes(trace, &pkt, sizeof(pkt));
- append_bytes(trace, padding, sizeof(padding));
-
- g_assert_false(read_tmp_trace(trace->data, trace->len,
- BTSNOOP_FLAG_PKLG_SUPPORT, 0, NULL,
- &size, &index, &opcode, &tv));
-
- g_byte_array_unref(trace);
-}
-
-static void test_pklg_rejects_small_capacity(void)
-{
- GByteArray *trace = g_byte_array_new();
- const uint8_t payload[] = { 0x01, 0x02, 0x03 };
- uint8_t data[4] = { 0xa5, 0xa5, 0xa5, 0xa5 };
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
-
- append_pklg_packet(trace, false, sizeof(payload), 0, 0x01, payload,
- sizeof(payload));
-
- g_assert_false(read_tmp_trace(trace->data, trace->len,
- BTSNOOP_FLAG_PKLG_SUPPORT, 2, data,
- &size, &index, &opcode, &tv));
- g_assert_cmpint(data[0], ==, 0xa5);
- g_assert_cmpint(data[1], ==, 0xa5);
- g_assert_cmpint(data[2], ==, 0xa5);
- g_assert_cmpint(data[3], ==, 0xa5);
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 3, &result));
g_byte_array_unref(trace);
}
-static void test_pklg_rejects_short_payload(void)
-{
- GByteArray *trace = g_byte_array_new();
- const uint8_t payload[] = { 0x01, 0x02, 0x03 };
- uint8_t data[4];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
-
- append_pklg_packet(trace, false, 4, 0, 0x01, payload,
- sizeof(payload));
-
- g_assert_false(read_tmp_trace(trace->data, trace->len,
- BTSNOOP_FLAG_PKLG_SUPPORT, sizeof(data),
- data, &size, &index, &opcode, &tv));
-
- g_byte_array_unref(trace);
-}
-
-static void test_pklg_type_map(void)
-{
- static const struct {
- uint8_t type;
- uint16_t index;
- uint16_t opcode;
- } cases[] = {
- { 0x02, 0x0000, BTSNOOP_OPCODE_ACL_TX_PKT },
- { 0x03, 0x0000, BTSNOOP_OPCODE_ACL_RX_PKT },
- { 0x08, 0x0000, BTSNOOP_OPCODE_SCO_TX_PKT },
- { 0x09, 0x0000, BTSNOOP_OPCODE_SCO_RX_PKT },
- { 0x12, 0x0000, BTSNOOP_OPCODE_ISO_TX_PKT },
- { 0x13, 0x0000, BTSNOOP_OPCODE_ISO_RX_PKT },
- { 0x0b, 0x0000, BTSNOOP_OPCODE_VENDOR_DIAG },
- { 0xfc, 0xffff, BTSNOOP_OPCODE_SYSTEM_NOTE },
- { 0xaa, 0xffff, 0xffff },
- };
- const uint8_t payload[] = { 0x00, 0x01, 0x02 };
- unsigned int i;
-
- for (i = 0; i < G_N_ELEMENTS(cases); i++) {
- GByteArray *trace = g_byte_array_new();
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
-
- append_pklg_packet(trace, false, sizeof(payload), 0,
- cases[i].type, payload,
- sizeof(payload));
-
- g_assert_true(read_tmp_trace(trace->data, trace->len,
- BTSNOOP_FLAG_PKLG_SUPPORT,
- sizeof(data), data, &size,
- &index, &opcode, &tv));
- g_assert_cmpint(index, ==, cases[i].index);
- g_assert_cmpint(opcode, ==, cases[i].opcode);
- g_byte_array_unref(trace);
- }
-}
-
static void test_btsnoop_truncation_fuzz(void)
{
GByteArray *trace = g_byte_array_new();
@@ -710,40 +497,10 @@ static void test_btsnoop_truncation_fuzz(void)
BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
for (len = 0; len < trace->len; len++) {
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
+ struct read_result result;
g_assert_false(read_tmp_trace(trace->data, len, 0,
- sizeof(data), data, &size,
- &index, &opcode, &tv));
- }
-
- g_byte_array_unref(trace);
-}
-
-static void test_pklg_truncation_fuzz(void)
-{
- GByteArray *trace = g_byte_array_new();
- const uint8_t payload[] = { 0x01, 0x02, 0x03 };
- size_t len;
-
- append_pklg_packet(trace, false, sizeof(payload), 0, 0x01, payload,
- sizeof(payload));
-
- for (len = 0; len < trace->len; len++) {
- uint8_t data[sizeof(payload)];
- struct timeval tv;
- uint16_t size = 0;
- uint16_t index = 0;
- uint16_t opcode = 0;
-
- g_assert_false(read_tmp_trace(trace->data, len,
- BTSNOOP_FLAG_PKLG_SUPPORT,
- sizeof(data), data, &size,
- &index, &opcode, &tv));
+ sizeof(payload), &result));
}
g_byte_array_unref(trace);
@@ -778,17 +535,10 @@ int main(int argc, char *argv[])
test_btsnoop_rejects_uart_short_type);
g_test_add_func("/btsnoop/payload/short",
test_btsnoop_rejects_short_payload);
- g_test_add_func("/pklg/big-endian/valid", test_pklg_big_endian_valid);
- g_test_add_func("/pklg/little-endian/valid",
- test_pklg_little_endian_valid);
- g_test_add_func("/pklg/length/short", test_pklg_rejects_short_length);
- g_test_add_func("/pklg/capacity/reject",
- test_pklg_rejects_small_capacity);
- g_test_add_func("/pklg/payload/short", test_pklg_rejects_short_payload);
- g_test_add_func("/pklg/type-map", test_pklg_type_map);
g_test_add_func("/btsnoop/fuzz/truncation",
test_btsnoop_truncation_fuzz);
- g_test_add_func("/pklg/fuzz/truncation", test_pklg_truncation_fuzz);
+
+ add_pklg_tests();
return g_test_run();
}
--
2.43.0
^ permalink raw reply related
* [PATCH BlueZ 1/2] shared: harden btsnoop trace parsing
From: Geraldo Netto @ 2026-06-20 19:22 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Geraldo Netto
---
Makefile.am | 6 +
monitor/analyze.c | 4 +-
monitor/control.c | 2 +-
src/shared/btsnoop.c | 303 +++++++++++------
src/shared/btsnoop.h | 6 +-
unit/test-btsnoop.c | 794 +++++++++++++++++++++++++++++++++++++++++++
unit/test-btsnoop.h | 3 +
7 files changed, 1009 insertions(+), 109 deletions(-)
create mode 100644 unit/test-btsnoop.c
create mode 100644 unit/test-btsnoop.h
diff --git a/Makefile.am b/Makefile.am
index 76c4ab5d4..4887934a9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -587,6 +587,12 @@ unit_tests += unit/test-textfile
unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
unit_test_textfile_LDADD = src/libshared-glib.la $(GLIB_LIBS)
+unit_tests += unit/test-btsnoop
+
+unit_test_btsnoop_SOURCES = unit/test-btsnoop.c \
+ src/shared/btsnoop.h src/shared/btsnoop.c
+unit_test_btsnoop_LDADD = src/libshared-glib.la $(GLIB_LIBS)
+
unit_tests += unit/test-crc
unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
diff --git a/monitor/analyze.c b/monitor/analyze.c
index de9c23603..c9400ceb3 100644
--- a/monitor/analyze.c
+++ b/monitor/analyze.c
@@ -1404,8 +1404,8 @@ void analyze_trace(const char *path)
struct timeval tv;
uint16_t index, opcode, pktlen;
- if (!btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode,
- buf, &pktlen))
+ if (!btsnoop_read_hci(btsnoop_file, &tv, &index,
+ &opcode, buf, sizeof(buf), &pktlen))
break;
switch (opcode) {
diff --git a/monitor/control.c b/monitor/control.c
index 83347d5db..975e1d117 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -1564,7 +1564,7 @@ void control_reader(const char *path, bool pager)
uint16_t index, opcode;
if (!btsnoop_read_hci(btsnoop_file, &tv, &index,
- &opcode, buf, &pktlen))
+ &opcode, buf, sizeof(buf), &pktlen))
break;
if (opcode == 0xffff)
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index 9ce2f2655..39960c4b8 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -13,6 +13,7 @@
#endif
#define _GNU_SOURCE
+#include <errno.h>
#include <endian.h>
#include <fcntl.h>
#include <unistd.h>
@@ -47,12 +48,16 @@ static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
static const uint32_t btsnoop_version = 1;
+#define BTSNOOP_EPOCH_OFFSET 0x00E03AB44A676000ull
+#define BTSNOOP_UNIX_TIME_OFFSET 946684800ll
+
struct pklg_pkt {
uint32_t len;
uint64_t ts;
uint8_t type;
} __attribute__ ((packed));
#define PKLG_PKT_SIZE (sizeof(struct pklg_pkt))
+#define PKLG_PAYLOAD_OFFSET (PKLG_PKT_SIZE - sizeof(uint32_t))
struct btsnoop {
int ref_count;
@@ -271,13 +276,14 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
if (!btsnoop_rotate(btsnoop))
return false;
- ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
+ ts = (tv->tv_sec - BTSNOOP_UNIX_TIME_OFFSET) * 1000000ll +
+ tv->tv_usec;
pkt.size = htobe32(size);
pkt.len = htobe32(size);
pkt.flags = htobe32(flags);
pkt.drops = htobe32(drops);
- pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
+ pkt.ts = htobe64(ts + BTSNOOP_EPOCH_OFFSET);
written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
if (written < 0)
@@ -324,6 +330,121 @@ static uint32_t get_flags_from_opcode(uint16_t opcode)
return 0xff;
}
+static ssize_t read_exact(int fd, void *data, size_t size)
+{
+ uint8_t *ptr = data;
+ size_t offset = 0;
+
+ while (offset < size) {
+ ssize_t len;
+
+ len = read(fd, ptr + offset, size - offset);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ if (len == 0)
+ break;
+
+ offset += len;
+ }
+
+ return offset;
+}
+
+static bool read_packet_data(struct btsnoop *btsnoop, void *data,
+ uint16_t data_size, uint32_t toread,
+ uint16_t *size)
+{
+ ssize_t len;
+
+ if (!size || (!data && toread)) {
+ btsnoop->aborted = true;
+ return false;
+ }
+
+ if (toread > data_size) {
+ btsnoop->aborted = true;
+ return false;
+ }
+
+ len = read_exact(btsnoop->fd, data, toread);
+ if (len != (ssize_t) toread) {
+ btsnoop->aborted = true;
+ return false;
+ }
+
+ *size = toread;
+
+ return true;
+}
+
+static bool decode_btsnoop_timestamp(uint64_t raw_ts, struct timeval *tv)
+{
+ uint64_t ts;
+
+ if (raw_ts < BTSNOOP_EPOCH_OFFSET)
+ return false;
+
+ ts = raw_ts - BTSNOOP_EPOCH_OFFSET;
+ tv->tv_sec = (ts / 1000000ll) + BTSNOOP_UNIX_TIME_OFFSET;
+ tv->tv_usec = ts % 1000000ll;
+
+ return true;
+}
+
+static void get_pklg_opcode(uint8_t type, uint16_t *index, uint16_t *opcode)
+{
+ switch (type) {
+ case 0x00:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_COMMAND_PKT;
+ break;
+ case 0x01:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_EVENT_PKT;
+ break;
+ case 0x02:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
+ break;
+ case 0x03:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
+ break;
+ case 0x08:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
+ break;
+ case 0x09:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
+ break;
+ case 0x12:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_ISO_TX_PKT;
+ break;
+ case 0x13:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_ISO_RX_PKT;
+ break;
+ case 0x0b:
+ *index = 0x0000;
+ *opcode = BTSNOOP_OPCODE_VENDOR_DIAG;
+ break;
+ case 0xfc:
+ *index = 0xffff;
+ *opcode = BTSNOOP_OPCODE_SYSTEM_NOTE;
+ break;
+ default:
+ *index = 0xffff;
+ *opcode = 0xffff;
+ break;
+ }
+}
+
bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
uint16_t index, uint16_t opcode, uint32_t drops,
const void *data, uint16_t size)
@@ -377,99 +498,53 @@ bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
return btsnoop_write(btsnoop, tv, flags, 0, data, size);
}
-static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
- uint16_t *index, uint16_t *opcode,
- void *data, uint16_t *size)
+static bool pklg_read_hci(struct btsnoop *btsnoop,
+ struct timeval *tv, uint16_t *index, uint16_t *opcode,
+ void *data, uint16_t data_size, uint16_t *size)
{
struct pklg_pkt pkt;
+ uint32_t pkt_len;
uint32_t toread;
uint64_t ts;
ssize_t len;
- len = read(btsnoop->fd, &pkt, PKLG_PKT_SIZE);
+ len = read_exact(btsnoop->fd, &pkt, PKLG_PKT_SIZE);
if (len == 0)
return false;
- if (len < 0 || len != PKLG_PKT_SIZE) {
+ if (len != PKLG_PKT_SIZE) {
btsnoop->aborted = true;
return false;
}
if (btsnoop->pklg_v2) {
- toread = le32toh(pkt.len) - (PKLG_PKT_SIZE - 4);
+ pkt_len = le32toh(pkt.len);
ts = le64toh(pkt.ts);
tv->tv_sec = ts & 0xffffffff;
tv->tv_usec = ts >> 32;
} else {
- toread = be32toh(pkt.len) - (PKLG_PKT_SIZE - 4);
+ pkt_len = be32toh(pkt.len);
ts = be64toh(pkt.ts);
tv->tv_sec = ts >> 32;
tv->tv_usec = ts & 0xffffffff;
}
- if (toread > BTSNOOP_MAX_PACKET_SIZE) {
- btsnoop->aborted = true;
- return false;
- }
-
- switch (pkt.type) {
- case 0x00:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_COMMAND_PKT;
- break;
- case 0x01:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_EVENT_PKT;
- break;
- case 0x02:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
- break;
- case 0x03:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
- break;
- case 0x08:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
- break;
- case 0x09:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
- break;
- case 0x12:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_ISO_TX_PKT;
- break;
- case 0x13:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_ISO_RX_PKT;
- break;
- case 0x0b:
- *index = 0x0000;
- *opcode = BTSNOOP_OPCODE_VENDOR_DIAG;
- break;
- case 0xfc:
- *index = 0xffff;
- *opcode = BTSNOOP_OPCODE_SYSTEM_NOTE;
- break;
- default:
- *index = 0xffff;
- *opcode = 0xffff;
- break;
+ if (pkt_len < PKLG_PAYLOAD_OFFSET) {
+ btsnoop->aborted = true;
+ return false;
}
- len = read(btsnoop->fd, data, toread);
- if (len < 0) {
+ toread = pkt_len - PKLG_PAYLOAD_OFFSET;
+ if (toread > BTSNOOP_MAX_PACKET_SIZE) {
btsnoop->aborted = true;
return false;
}
- *size = toread;
+ get_pklg_opcode(pkt.type, index, opcode);
- return true;
+ return read_packet_data(btsnoop, data, data_size, toread, size);
}
static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
@@ -512,84 +587,106 @@ static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
return 0xffff;
}
-bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
- uint16_t *index, uint16_t *opcode,
- void *data, uint16_t *size)
+static bool read_uart_type(struct btsnoop *btsnoop, uint32_t *toread,
+ uint8_t *type)
{
- struct btsnoop_pkt pkt;
- uint32_t toread, flags;
- uint64_t ts;
- uint8_t pkt_type;
ssize_t len;
- if (!btsnoop || btsnoop->aborted)
- return false;
-
- if (btsnoop->pklg_format)
- return pklg_read_hci(btsnoop, tv, index, opcode, data, size);
-
- len = read(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
- if (len == 0)
- return false;
-
- if (len < 0 || len != BTSNOOP_PKT_SIZE) {
+ if (!*toread) {
btsnoop->aborted = true;
return false;
}
- toread = be32toh(pkt.len);
- if (toread > BTSNOOP_MAX_PACKET_SIZE) {
+ len = read_exact(btsnoop->fd, type, 1);
+ if (len != 1) {
btsnoop->aborted = true;
return false;
}
- flags = be32toh(pkt.flags);
+ (*toread)--;
- ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
- tv->tv_sec = (ts / 1000000ll) + 946684800ll;
- tv->tv_usec = ts % 1000000ll;
+ return true;
+}
+
+static bool decode_btsnoop_record(struct btsnoop *btsnoop, uint32_t flags,
+ uint32_t *toread, uint16_t *index,
+ uint16_t *opcode)
+{
+ uint8_t pkt_type;
switch (btsnoop->format) {
case BTSNOOP_FORMAT_HCI:
*index = 0;
*opcode = get_opcode_from_flags(0xff, flags);
- break;
-
+ return true;
case BTSNOOP_FORMAT_UART:
- len = read(btsnoop->fd, &pkt_type, 1);
- if (len < 0) {
- btsnoop->aborted = true;
+ if (!read_uart_type(btsnoop, toread, &pkt_type))
return false;
- }
- toread--;
*index = 0;
*opcode = get_opcode_from_flags(pkt_type, flags);
- break;
-
+ return true;
case BTSNOOP_FORMAT_MONITOR:
*index = flags >> 16;
*opcode = flags & 0xffff;
- break;
-
+ return true;
default:
btsnoop->aborted = true;
return false;
}
+}
- len = read(btsnoop->fd, data, toread);
- if (len < 0) {
+bool btsnoop_read_hci(struct btsnoop *btsnoop,
+ struct timeval *tv, uint16_t *index, uint16_t *opcode,
+ void *data, uint16_t data_size, uint16_t *size)
+{
+ struct btsnoop_pkt pkt;
+ uint32_t toread, flags;
+ ssize_t len;
+
+ if (!btsnoop || !tv || !index || !opcode || !size || btsnoop->aborted)
+ return false;
+
+ if (btsnoop->pklg_format)
+ return pklg_read_hci(btsnoop, tv, index, opcode,
+ data, data_size, size);
+
+ len = read_exact(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
+ if (len == 0)
+ return false;
+
+ if (len != BTSNOOP_PKT_SIZE) {
btsnoop->aborted = true;
return false;
}
- *size = toread;
+ toread = be32toh(pkt.len);
+ if (toread > BTSNOOP_MAX_PACKET_SIZE) {
+ btsnoop->aborted = true;
+ return false;
+ }
- return true;
+ flags = be32toh(pkt.flags);
+
+ if (!decode_btsnoop_timestamp(be64toh(pkt.ts), tv)) {
+ btsnoop->aborted = true;
+ return false;
+ }
+
+ if (!decode_btsnoop_record(btsnoop, flags, &toread, index, opcode))
+ return false;
+
+ return read_packet_data(btsnoop, data, data_size, toread, size);
}
bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv,
- uint16_t *frequency, void *data, uint16_t *size)
+ uint16_t *frequency, void *data, uint16_t *size)
{
+ (void) btsnoop;
+ (void) tv;
+ (void) frequency;
+ (void) data;
+ (void) size;
+
return false;
}
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index c24755d56..796604c58 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -106,8 +106,8 @@ bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
uint16_t frequency, const void *data, uint16_t size);
-bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
- uint16_t *index, uint16_t *opcode,
- void *data, uint16_t *size);
+bool btsnoop_read_hci(struct btsnoop *btsnoop,
+ struct timeval *tv, uint16_t *index, uint16_t *opcode,
+ void *data, uint16_t data_size, uint16_t *size);
bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv,
uint16_t *frequency, void *data, uint16_t *size);
diff --git a/unit/test-btsnoop.c b/unit/test-btsnoop.c
new file mode 100644
index 000000000..710209097
--- /dev/null
+++ b/unit/test-btsnoop.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "src/shared/att-types.h"
+#include "src/shared/btsnoop.h"
+#include "unit/test-btsnoop.h"
+
+#define BTSNOOP_EPOCH_OFFSET 0x00E03AB44A676000ull
+#define PKLG_PAYLOAD_OFFSET 9
+
+struct test_btsnoop_hdr {
+ uint8_t id[8];
+ uint32_t version;
+ uint32_t type;
+} __packed;
+
+struct test_btsnoop_pkt {
+ uint32_t size;
+ uint32_t len;
+ uint32_t flags;
+ uint32_t drops;
+ uint64_t ts;
+} __packed;
+
+struct test_pklg_pkt {
+ uint32_t len;
+ uint64_t ts;
+ uint8_t type;
+} __packed;
+
+static const uint8_t btsnoop_id[] = {
+ 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00
+};
+
+static void append_bytes(GByteArray *array, const void *data, size_t size)
+{
+ if (!size)
+ return;
+
+ g_byte_array_append(array, data, size);
+}
+
+static void append_btsnoop_header(GByteArray *array, uint32_t format)
+{
+ struct test_btsnoop_hdr hdr;
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(1);
+ hdr.type = htobe32(format);
+
+ append_bytes(array, &hdr, sizeof(hdr));
+}
+
+static void append_btsnoop_packet(GByteArray *array, uint32_t len,
+ uint32_t flags, uint64_t ts,
+ const void *data, size_t data_len)
+{
+ struct test_btsnoop_pkt pkt;
+
+ pkt.size = htobe32(len);
+ pkt.len = htobe32(len);
+ pkt.flags = htobe32(flags);
+ pkt.drops = 0;
+ pkt.ts = htobe64(ts);
+
+ append_bytes(array, &pkt, sizeof(pkt));
+ append_bytes(array, data, data_len);
+}
+
+static void append_pklg_packet(GByteArray *array, bool little_endian,
+ uint32_t payload_len, uint64_t ts,
+ uint8_t type, const void *data, size_t data_len)
+{
+ struct test_pklg_pkt pkt;
+ uint32_t len = PKLG_PAYLOAD_OFFSET + payload_len;
+
+ pkt.len = little_endian ? htole32(len) : htobe32(len);
+ pkt.ts = little_endian ? htole64(ts) : htobe64(ts);
+ pkt.type = type;
+
+ append_bytes(array, &pkt, sizeof(pkt));
+ append_bytes(array, data, data_len);
+}
+
+static char *write_tmp_trace(const void *data, size_t size)
+{
+ char *path = NULL;
+ ssize_t written;
+ int fd;
+
+ fd = g_file_open_tmp("bluez-btsnoop-XXXXXX", &path, NULL);
+ g_assert(fd >= 0);
+ written = write(fd, data, size);
+ g_assert_cmpint(written, ==, (ssize_t) size);
+ g_assert_cmpint(close(fd), ==, 0);
+
+ return path;
+}
+
+static char *new_tmp_path(void)
+{
+ char *path = NULL;
+ int fd;
+
+ fd = g_file_open_tmp("bluez-btsnoop-XXXXXX", &path, NULL);
+ g_assert(fd >= 0);
+ g_assert_cmpint(close(fd), ==, 0);
+ unlink(path);
+
+ return path;
+}
+
+static void unlink_rotated(const char *path, unsigned int count)
+{
+ unsigned int i;
+
+ for (i = 0; i <= count; i++) {
+ char *name;
+
+ name = g_strdup_printf("%s.%u", path, i);
+ unlink(name);
+ g_free(name);
+ }
+}
+
+static bool read_tmp_trace(const void *trace, size_t trace_len,
+ unsigned long flags, uint16_t data_size,
+ uint8_t *data, uint16_t *size,
+ uint16_t *index, uint16_t *opcode,
+ struct timeval *tv)
+{
+ struct btsnoop *btsnoop;
+ char *path;
+ bool result;
+
+ path = write_tmp_trace(trace, trace_len);
+ btsnoop = btsnoop_open(path, flags);
+ unlink(path);
+ g_free(path);
+
+ if (!btsnoop)
+ return false;
+
+ result = btsnoop_read_hci(btsnoop, tv, index, opcode, data,
+ data_size, size);
+ btsnoop_unref(btsnoop);
+
+ return result;
+}
+
+static bool read_trace_file(const char *path, unsigned long flags,
+ uint8_t *data, uint16_t data_size,
+ uint16_t *size, uint16_t *index,
+ uint16_t *opcode, struct timeval *tv)
+{
+ struct btsnoop *btsnoop;
+ bool result;
+
+ btsnoop = btsnoop_open(path, flags);
+ g_assert_nonnull(btsnoop);
+
+ result = btsnoop_read_hci(btsnoop, tv, index, opcode, data,
+ data_size, size);
+ btsnoop_unref(btsnoop);
+
+ return result;
+}
+
+static void test_btsnoop_hci_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0xffff;
+ uint16_t opcode = 0xffff;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
+ append_btsnoop_packet(trace, sizeof(payload), 0x02,
+ BTSNOOP_EPOCH_OFFSET + 1234567, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0, sizeof(data),
+ data, &size, &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 0);
+ g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
+ g_assert_cmpint(size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
+ g_assert_cmpint(tv.tv_sec, ==, 946684801);
+ g_assert_cmpint(tv.tv_usec, ==, 234567);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_create_invalid_args(void)
+{
+ char *path = new_tmp_path();
+
+ g_assert_null(btsnoop_create(path, 0, 1, BTSNOOP_FORMAT_HCI));
+ g_assert_null(btsnoop_create("/tmp/bluez/no/such/path", 0, 0,
+ BTSNOOP_FORMAT_HCI));
+ g_assert_null(btsnoop_ref(NULL));
+ btsnoop_unref(NULL);
+ g_assert_cmpint(btsnoop_get_format(NULL), ==, BTSNOOP_FORMAT_INVALID);
+
+ g_free(path);
+}
+
+static void test_btsnoop_open_invalid_headers(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ struct test_btsnoop_hdr hdr;
+ char *path;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
+ ((struct test_btsnoop_hdr *) trace->data)->version = htobe32(2);
+ path = write_tmp_trace(trace->data, trace->len);
+ g_assert_null(btsnoop_open(path, 0));
+ unlink(path);
+ g_free(path);
+ g_byte_array_set_size(trace, 0);
+
+ memset(&hdr, 0x55, sizeof(hdr));
+ append_bytes(trace, &hdr, sizeof(hdr));
+ path = write_tmp_trace(trace->data, trace->len);
+ g_assert_null(btsnoop_open(path, 0));
+ g_assert_null(btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT));
+ unlink(path);
+ g_free(path);
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_write_hci_roundtrip(void)
+{
+ const uint8_t command[] = { 0x01, 0x02, 0x03 };
+ const uint8_t event[] = { 0x04, 0x05 };
+ uint8_t data[sizeof(command)];
+ struct btsnoop *btsnoop;
+ struct timeval tv = { .tv_sec = 946684802, .tv_usec = 345678 };
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+ char *path = new_tmp_path();
+
+ btsnoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_HCI);
+ g_assert_nonnull(btsnoop);
+ g_assert_cmpint(btsnoop_get_format(btsnoop), ==, BTSNOOP_FORMAT_HCI);
+ g_assert_true(btsnoop_write_hci(btsnoop, &tv, 0,
+ BTSNOOP_OPCODE_COMMAND_PKT, 0,
+ command, sizeof(command)));
+ g_assert_true(btsnoop_write_hci(btsnoop, &tv, 0,
+ BTSNOOP_OPCODE_EVENT_PKT, 0,
+ event, sizeof(event)));
+ g_assert_false(btsnoop_write_hci(btsnoop, &tv, 1,
+ BTSNOOP_OPCODE_COMMAND_PKT, 0,
+ command, sizeof(command)));
+ g_assert_false(btsnoop_write_hci(btsnoop, &tv, 0,
+ BTSNOOP_OPCODE_NEW_INDEX, 0,
+ command, sizeof(command)));
+ btsnoop_unref(btsnoop);
+
+ g_assert_true(read_trace_file(path, 0, data, sizeof(data), &size,
+ &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 0);
+ g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
+ g_assert_cmpint(size, ==, sizeof(command));
+ g_assert_cmpint(memcmp(data, command, sizeof(command)), ==, 0);
+
+ btsnoop = btsnoop_open(path, 0);
+ g_assert_nonnull(btsnoop);
+ g_assert_true(btsnoop_read_hci(btsnoop, &tv, &index, &opcode, data,
+ sizeof(data), &size));
+ g_assert_true(btsnoop_read_hci(btsnoop, &tv, &index, &opcode, data,
+ sizeof(data), &size));
+ g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
+ g_assert_cmpint(size, ==, sizeof(event));
+ g_assert_cmpint(memcmp(data, event, sizeof(event)), ==, 0);
+ g_assert_false(btsnoop_read_hci(btsnoop, &tv, &index, &opcode, data,
+ sizeof(data), &size));
+ btsnoop_unref(btsnoop);
+
+ unlink(path);
+ g_free(path);
+}
+
+static void test_btsnoop_write_monitor_roundtrip(void)
+{
+ const uint8_t payload[] = { 0xaa, 0xbb };
+ uint8_t data[sizeof(payload)];
+ struct btsnoop *btsnoop;
+ struct timeval tv = { .tv_sec = 946684800, .tv_usec = 0 };
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+ char *path = new_tmp_path();
+
+ btsnoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_MONITOR);
+ g_assert_nonnull(btsnoop);
+ g_assert_true(btsnoop_write_hci(btsnoop, &tv, 7, 0x1234, 0,
+ payload, sizeof(payload)));
+ btsnoop_unref(btsnoop);
+
+ g_assert_true(read_trace_file(path, 0, data, sizeof(data), &size,
+ &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 7);
+ g_assert_cmpint(opcode, ==, 0x1234);
+ g_assert_cmpint(size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
+
+ unlink(path);
+ g_free(path);
+}
+
+static void test_btsnoop_write_phy_and_rotate(void)
+{
+ const uint8_t payload[] = { 0x01 };
+ struct btsnoop *btsnoop;
+ struct timeval tv = { .tv_sec = 946684800, .tv_usec = 0 };
+ char *path = new_tmp_path();
+
+ g_assert_false(btsnoop_write(NULL, &tv, 0, 0, payload,
+ sizeof(payload)));
+
+ btsnoop = btsnoop_create(path, 24, 1, BTSNOOP_FORMAT_SIMULATOR);
+ g_assert_nonnull(btsnoop);
+ g_assert_true(btsnoop_write_phy(btsnoop, &tv, 2402, payload,
+ sizeof(payload)));
+ btsnoop_unref(btsnoop);
+
+ btsnoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_HCI);
+ g_assert_nonnull(btsnoop);
+ g_assert_false(btsnoop_write_phy(btsnoop, &tv, 2402, payload,
+ sizeof(payload)));
+ btsnoop_unref(btsnoop);
+
+ unlink(path);
+ unlink_rotated(path, 1);
+ g_free(path);
+}
+
+static void test_btsnoop_monitor_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0xaa, 0xbb };
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0xffff;
+ uint16_t opcode = 0xffff;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_MONITOR);
+ append_btsnoop_packet(trace, sizeof(payload), 0x00051234,
+ BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0, sizeof(data),
+ data, &size, &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 5);
+ g_assert_cmpint(opcode, ==, 0x1234);
+ g_assert_cmpint(size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_uart_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x04, 0x0e, 0x01, 0x00 };
+ uint8_t data[sizeof(payload) - 1];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0xffff;
+ uint16_t opcode = 0xffff;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
+ append_btsnoop_packet(trace, sizeof(payload), 0,
+ BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0, sizeof(data),
+ data, &size, &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 0);
+ g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
+ g_assert_cmpint(size, ==, sizeof(payload) - 1);
+ g_assert_cmpint(memcmp(data, payload + 1, sizeof(data)), ==, 0);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_uart_opcode_map(void)
+{
+ static const struct {
+ uint8_t type;
+ uint32_t flags;
+ uint16_t opcode;
+ } cases[] = {
+ { 0x01, 0x00, BTSNOOP_OPCODE_COMMAND_PKT },
+ { 0x02, 0x00, BTSNOOP_OPCODE_ACL_TX_PKT },
+ { 0x02, 0x01, BTSNOOP_OPCODE_ACL_RX_PKT },
+ { 0x03, 0x00, BTSNOOP_OPCODE_SCO_TX_PKT },
+ { 0x03, 0x01, BTSNOOP_OPCODE_SCO_RX_PKT },
+ { 0x05, 0x00, BTSNOOP_OPCODE_ISO_TX_PKT },
+ { 0x05, 0x01, BTSNOOP_OPCODE_ISO_RX_PKT },
+ { 0x99, 0x00, 0xffff },
+ };
+ unsigned int i;
+
+ for (i = 0; i < G_N_ELEMENTS(cases); i++) {
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { cases[i].type, 0x00 };
+ uint8_t data[1];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
+ append_btsnoop_packet(trace, sizeof(payload), cases[i].flags,
+ BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len, 0,
+ sizeof(data), data, &size,
+ &index, &opcode, &tv));
+ g_assert_cmpint(opcode, ==, cases[i].opcode);
+ g_byte_array_unref(trace);
+ }
+}
+
+static void test_btsnoop_rejects_small_capacity(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ uint8_t data[4] = { 0xa5, 0xa5, 0xa5, 0xa5 };
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
+ append_btsnoop_packet(trace, sizeof(payload), 0x02,
+ BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 2, data,
+ &size, &index, &opcode, &tv));
+ g_assert_cmpint(data[0], ==, 0xa5);
+ g_assert_cmpint(data[1], ==, 0xa5);
+ g_assert_cmpint(data[2], ==, 0xa5);
+ g_assert_cmpint(data[3], ==, 0xa5);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_rejects_timestamp_underflow(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
+ append_btsnoop_packet(trace, 0, 0x02, BTSNOOP_EPOCH_OFFSET - 1,
+ NULL, 0);
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, NULL,
+ &size, &index, &opcode, &tv));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_rejects_uart_zero_length(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
+ append_btsnoop_packet(trace, 0, 0, BTSNOOP_EPOCH_OFFSET, NULL, 0);
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, NULL,
+ &size, &index, &opcode, &tv));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_rejects_uart_short_type(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_UART);
+ append_btsnoop_packet(trace, 1, 0, BTSNOOP_EPOCH_OFFSET, NULL, 0);
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0, 0, NULL,
+ &size, &index, &opcode, &tv));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_btsnoop_rejects_short_payload(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02 };
+ uint8_t data[3];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
+ append_btsnoop_packet(trace, 3, 0x02, BTSNOOP_EPOCH_OFFSET,
+ payload, sizeof(payload));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len, 0,
+ sizeof(data), data, &size,
+ &index, &opcode, &tv));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_big_endian_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x0e, 0x01, 0x00 };
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0xffff;
+ uint16_t opcode = 0xffff;
+
+ append_pklg_packet(trace, false, sizeof(payload),
+ ((uint64_t) 123 << 32) | 456, 0x01, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len,
+ BTSNOOP_FLAG_PKLG_SUPPORT, sizeof(data),
+ data, &size, &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 0);
+ g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_EVENT_PKT);
+ g_assert_cmpint(size, ==, sizeof(payload));
+ g_assert_cmpint(memcmp(data, payload, sizeof(payload)), ==, 0);
+ g_assert_cmpint(tv.tv_sec, ==, 123);
+ g_assert_cmpint(tv.tv_usec, ==, 456);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_little_endian_valid(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0xffff;
+ uint16_t opcode = 0xffff;
+
+ append_pklg_packet(trace, true, sizeof(payload),
+ ((uint64_t) 456 << 32) | 123, 0x00, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len,
+ BTSNOOP_FLAG_PKLG_SUPPORT, sizeof(data),
+ data, &size, &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, 0);
+ g_assert_cmpint(opcode, ==, BTSNOOP_OPCODE_COMMAND_PKT);
+ g_assert_cmpint(size, ==, sizeof(payload));
+ g_assert_cmpint(data[0], ==, payload[0]);
+ g_assert_cmpint(tv.tv_sec, ==, 123);
+ g_assert_cmpint(tv.tv_usec, ==, 456);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_rejects_short_length(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ struct test_pklg_pkt pkt;
+ const uint8_t padding[] = { 0x00, 0x00, 0x00 };
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ pkt.len = htobe32(PKLG_PAYLOAD_OFFSET - 1);
+ pkt.ts = 0;
+ pkt.type = 0x01;
+
+ append_bytes(trace, &pkt, sizeof(pkt));
+ append_bytes(trace, padding, sizeof(padding));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len,
+ BTSNOOP_FLAG_PKLG_SUPPORT, 0, NULL,
+ &size, &index, &opcode, &tv));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_rejects_small_capacity(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ uint8_t data[4] = { 0xa5, 0xa5, 0xa5, 0xa5 };
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_pklg_packet(trace, false, sizeof(payload), 0, 0x01, payload,
+ sizeof(payload));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len,
+ BTSNOOP_FLAG_PKLG_SUPPORT, 2, data,
+ &size, &index, &opcode, &tv));
+ g_assert_cmpint(data[0], ==, 0xa5);
+ g_assert_cmpint(data[1], ==, 0xa5);
+ g_assert_cmpint(data[2], ==, 0xa5);
+ g_assert_cmpint(data[3], ==, 0xa5);
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_rejects_short_payload(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ uint8_t data[4];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_pklg_packet(trace, false, 4, 0, 0x01, payload,
+ sizeof(payload));
+
+ g_assert_false(read_tmp_trace(trace->data, trace->len,
+ BTSNOOP_FLAG_PKLG_SUPPORT, sizeof(data),
+ data, &size, &index, &opcode, &tv));
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_type_map(void)
+{
+ static const struct {
+ uint8_t type;
+ uint16_t index;
+ uint16_t opcode;
+ } cases[] = {
+ { 0x02, 0x0000, BTSNOOP_OPCODE_ACL_TX_PKT },
+ { 0x03, 0x0000, BTSNOOP_OPCODE_ACL_RX_PKT },
+ { 0x08, 0x0000, BTSNOOP_OPCODE_SCO_TX_PKT },
+ { 0x09, 0x0000, BTSNOOP_OPCODE_SCO_RX_PKT },
+ { 0x12, 0x0000, BTSNOOP_OPCODE_ISO_TX_PKT },
+ { 0x13, 0x0000, BTSNOOP_OPCODE_ISO_RX_PKT },
+ { 0x0b, 0x0000, BTSNOOP_OPCODE_VENDOR_DIAG },
+ { 0xfc, 0xffff, BTSNOOP_OPCODE_SYSTEM_NOTE },
+ { 0xaa, 0xffff, 0xffff },
+ };
+ const uint8_t payload[] = { 0x00, 0x01, 0x02 };
+ unsigned int i;
+
+ for (i = 0; i < G_N_ELEMENTS(cases); i++) {
+ GByteArray *trace = g_byte_array_new();
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ append_pklg_packet(trace, false, sizeof(payload), 0,
+ cases[i].type, payload,
+ sizeof(payload));
+
+ g_assert_true(read_tmp_trace(trace->data, trace->len,
+ BTSNOOP_FLAG_PKLG_SUPPORT,
+ sizeof(data), data, &size,
+ &index, &opcode, &tv));
+ g_assert_cmpint(index, ==, cases[i].index);
+ g_assert_cmpint(opcode, ==, cases[i].opcode);
+ g_byte_array_unref(trace);
+ }
+}
+
+static void test_btsnoop_truncation_fuzz(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ size_t len;
+
+ append_btsnoop_header(trace, BTSNOOP_FORMAT_HCI);
+ append_btsnoop_packet(trace, sizeof(payload), 0x02,
+ BTSNOOP_EPOCH_OFFSET, payload, sizeof(payload));
+
+ for (len = 0; len < trace->len; len++) {
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ g_assert_false(read_tmp_trace(trace->data, len, 0,
+ sizeof(data), data, &size,
+ &index, &opcode, &tv));
+ }
+
+ g_byte_array_unref(trace);
+}
+
+static void test_pklg_truncation_fuzz(void)
+{
+ GByteArray *trace = g_byte_array_new();
+ const uint8_t payload[] = { 0x01, 0x02, 0x03 };
+ size_t len;
+
+ append_pklg_packet(trace, false, sizeof(payload), 0, 0x01, payload,
+ sizeof(payload));
+
+ for (len = 0; len < trace->len; len++) {
+ uint8_t data[sizeof(payload)];
+ struct timeval tv;
+ uint16_t size = 0;
+ uint16_t index = 0;
+ uint16_t opcode = 0;
+
+ g_assert_false(read_tmp_trace(trace->data, len,
+ BTSNOOP_FLAG_PKLG_SUPPORT,
+ sizeof(data), data, &size,
+ &index, &opcode, &tv));
+ }
+
+ g_byte_array_unref(trace);
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/btsnoop/hci/valid", test_btsnoop_hci_valid);
+ g_test_add_func("/btsnoop/create/invalid",
+ test_btsnoop_create_invalid_args);
+ g_test_add_func("/btsnoop/open/invalid",
+ test_btsnoop_open_invalid_headers);
+ g_test_add_func("/btsnoop/write/hci-roundtrip",
+ test_btsnoop_write_hci_roundtrip);
+ g_test_add_func("/btsnoop/write/monitor-roundtrip",
+ test_btsnoop_write_monitor_roundtrip);
+ g_test_add_func("/btsnoop/write/phy-and-rotate",
+ test_btsnoop_write_phy_and_rotate);
+ g_test_add_func("/btsnoop/monitor/valid", test_btsnoop_monitor_valid);
+ g_test_add_func("/btsnoop/uart/valid", test_btsnoop_uart_valid);
+ g_test_add_func("/btsnoop/uart/opcode-map",
+ test_btsnoop_uart_opcode_map);
+ g_test_add_func("/btsnoop/capacity/reject",
+ test_btsnoop_rejects_small_capacity);
+ g_test_add_func("/btsnoop/timestamp/underflow",
+ test_btsnoop_rejects_timestamp_underflow);
+ g_test_add_func("/btsnoop/uart/zero-length",
+ test_btsnoop_rejects_uart_zero_length);
+ g_test_add_func("/btsnoop/uart/short-type",
+ test_btsnoop_rejects_uart_short_type);
+ g_test_add_func("/btsnoop/payload/short",
+ test_btsnoop_rejects_short_payload);
+ g_test_add_func("/pklg/big-endian/valid", test_pklg_big_endian_valid);
+ g_test_add_func("/pklg/little-endian/valid",
+ test_pklg_little_endian_valid);
+ g_test_add_func("/pklg/length/short", test_pklg_rejects_short_length);
+ g_test_add_func("/pklg/capacity/reject",
+ test_pklg_rejects_small_capacity);
+ g_test_add_func("/pklg/payload/short", test_pklg_rejects_short_payload);
+ g_test_add_func("/pklg/type-map", test_pklg_type_map);
+ g_test_add_func("/btsnoop/fuzz/truncation",
+ test_btsnoop_truncation_fuzz);
+ g_test_add_func("/pklg/fuzz/truncation", test_pklg_truncation_fuzz);
+
+ return g_test_run();
+}
diff --git a/unit/test-btsnoop.h b/unit/test-btsnoop.h
new file mode 100644
index 000000000..9a12f3e71
--- /dev/null
+++ b/unit/test-btsnoop.h
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+void add_pklg_tests(void);
--
2.43.0
^ permalink raw reply related
* [PATCH BlueZ 2/2] audio: reduce a2dp parser complexity
From: Geraldo Netto @ 2026-06-20 19:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Geraldo Netto
In-Reply-To: <20260620191735.2675946-1-geraldonetto@gmail.com>
---
profiles/audio/a2dp-helpers.c | 55 +++++++++++++++++++++++++++--------
1 file changed, 43 insertions(+), 12 deletions(-)
diff --git a/profiles/audio/a2dp-helpers.c b/profiles/audio/a2dp-helpers.c
index 035236df6..09dc6db91 100644
--- a/profiles/audio/a2dp-helpers.c
+++ b/profiles/audio/a2dp-helpers.c
@@ -74,6 +74,46 @@ static bool parse_caps(const char *value, uint8_t *caps, size_t caps_len,
return true;
}
+static bool has_delay_field(const char *value)
+{
+ return value[0] != '\0' && value[1] != '\0' &&
+ g_ascii_isxdigit(value[0]) &&
+ g_ascii_isxdigit(value[1]) &&
+ value[2] == ':';
+}
+
+static bool parse_endpoint_header(const char **value, uint8_t *type,
+ uint8_t *codec)
+{
+ if (!parse_hex_byte(value, type) || !parse_colon(value))
+ return false;
+
+ if (!parse_hex_byte(value, codec) || !parse_colon(value))
+ return false;
+
+ return true;
+}
+
+static bool parse_delay_field(const char **value, uint8_t *delay)
+{
+ *delay = 0;
+
+ if (!has_delay_field(*value))
+ return true;
+
+ parse_hex_byte(value, delay);
+ parse_colon(value);
+
+ return *delay <= 1;
+}
+
+static bool valid_endpoint_args(const char *value,
+ const uint8_t *type, const uint8_t *codec,
+ const bool *delay_reporting, const size_t *size)
+{
+ return value && type && codec && delay_reporting && size;
+}
+
bool a2dp_parse_capabilities_array(DBusMessageIter *value,
uint8_t **caps, int *size)
{
@@ -106,27 +146,18 @@ bool a2dp_parse_persisted_endpoint(const char *value, uint8_t *type,
const char *pos;
uint8_t delay = 0;
- if (!value || !type || !codec || !delay_reporting || !size)
+ if (!valid_endpoint_args(value, type, codec, delay_reporting, size))
return false;
*size = 0;
pos = value;
- if (!parse_hex_byte(&pos, type) || !parse_colon(&pos))
+ if (!parse_endpoint_header(&pos, type, codec))
return false;
- if (!parse_hex_byte(&pos, codec) || !parse_colon(&pos))
+ if (!parse_delay_field(&pos, &delay))
return false;
- if (pos[0] != '\0' && pos[1] != '\0' &&
- g_ascii_isxdigit(pos[0]) && g_ascii_isxdigit(pos[1]) &&
- pos[2] == ':') {
- parse_hex_byte(&pos, &delay);
- parse_colon(&pos);
- if (delay > 1)
- return false;
- }
-
if (!parse_caps(pos, caps, caps_len, size))
return false;
--
2.43.0
^ permalink raw reply related
* [PATCH BlueZ 1/2] audio: harden a2dp parsers
From: Geraldo Netto @ 2026-06-20 19:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Geraldo Netto
In-Reply-To: <20260620191735.2675946-1-geraldonetto@gmail.com>
---
Makefile.am | 7 +
Makefile.plugins | 1 +
profiles/audio/a2dp-helpers.c | 136 ++++++++++++++++
profiles/audio/a2dp-helpers.h | 20 +++
profiles/audio/a2dp.c | 86 +++++-----
unit/test-a2dp.c | 288 ++++++++++++++++++++++++++++++++++
6 files changed, 489 insertions(+), 49 deletions(-)
create mode 100644 profiles/audio/a2dp-helpers.c
create mode 100644 profiles/audio/a2dp-helpers.h
create mode 100644 unit/test-a2dp.c
diff --git a/Makefile.am b/Makefile.am
index 76c4ab5d4..35871cc57 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -642,6 +642,13 @@ unit_test_avdtp_SOURCES = unit/test-avdtp.c \
unit/avdtp.c unit/avdtp.h
unit_test_avdtp_LDADD = src/libshared-glib.la $(GLIB_LIBS)
+unit_tests += unit/test-a2dp
+
+unit_test_a2dp_SOURCES = unit/test-a2dp.c \
+ profiles/audio/a2dp-helpers.c \
+ profiles/audio/a2dp-helpers.h
+unit_test_a2dp_LDADD = src/libshared-glib.la $(GLIB_LIBS) $(DBUS_LIBS)
+
unit_tests += unit/test-avctp
unit_test_avctp_SOURCES = unit/test-avctp.c \
diff --git a/Makefile.plugins b/Makefile.plugins
index ac667beda..57400d877 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -27,6 +27,7 @@ builtin_modules += a2dp
builtin_sources += profiles/audio/source.h profiles/audio/source.c \
profiles/audio/sink.h profiles/audio/sink.c \
profiles/audio/a2dp.h profiles/audio/a2dp.c \
+ profiles/audio/a2dp-helpers.h profiles/audio/a2dp-helpers.c \
profiles/audio/avdtp.h profiles/audio/avdtp.c \
profiles/audio/a2dp-codecs.h
endif
diff --git a/profiles/audio/a2dp-helpers.c b/profiles/audio/a2dp-helpers.c
new file mode 100644
index 000000000..035236df6
--- /dev/null
+++ b/profiles/audio/a2dp-helpers.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "a2dp-helpers.h"
+
+static bool parse_hex_byte(const char **value, uint8_t *byte)
+{
+ int high;
+ int low;
+
+ if ((*value)[0] == '\0' || (*value)[1] == '\0')
+ return false;
+
+ high = g_ascii_xdigit_value((*value)[0]);
+ low = g_ascii_xdigit_value((*value)[1]);
+ if (high < 0 || low < 0)
+ return false;
+
+ *byte = high << 4 | low;
+ *value += 2;
+
+ return true;
+}
+
+static bool parse_colon(const char **value)
+{
+ if (**value != ':')
+ return false;
+
+ (*value)++;
+
+ return true;
+}
+
+static bool parse_caps(const char *value, uint8_t *caps, size_t caps_len,
+ size_t *size)
+{
+ size_t len;
+ size_t i;
+
+ if (!value || !caps || !size)
+ return false;
+
+ *size = 0;
+
+ len = strlen(value);
+ if (!len || len % 2 || len / 2 > caps_len)
+ return false;
+
+ for (i = 0; i < len; i++) {
+ if (!g_ascii_isxdigit(value[i]))
+ return false;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ const char *pos = value + i;
+
+ parse_hex_byte(&pos, &caps[i / 2]);
+ }
+
+ *size = len / 2;
+
+ return true;
+}
+
+bool a2dp_parse_capabilities_array(DBusMessageIter *value,
+ uint8_t **caps, int *size)
+{
+ DBusMessageIter array;
+
+ if (!value || !caps || !size)
+ return false;
+
+ *caps = NULL;
+ *size = 0;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+ return false;
+
+ if (dbus_message_iter_get_element_type(value) != DBUS_TYPE_BYTE)
+ return false;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, caps, size);
+
+ return *caps && *size > 0;
+}
+
+bool a2dp_parse_persisted_endpoint(const char *value, uint8_t *type,
+ uint8_t *codec,
+ bool *delay_reporting,
+ uint8_t *caps, size_t caps_len,
+ size_t *size)
+{
+ const char *pos;
+ uint8_t delay = 0;
+
+ if (!value || !type || !codec || !delay_reporting || !size)
+ return false;
+
+ *size = 0;
+
+ pos = value;
+ if (!parse_hex_byte(&pos, type) || !parse_colon(&pos))
+ return false;
+
+ if (!parse_hex_byte(&pos, codec) || !parse_colon(&pos))
+ return false;
+
+ if (pos[0] != '\0' && pos[1] != '\0' &&
+ g_ascii_isxdigit(pos[0]) && g_ascii_isxdigit(pos[1]) &&
+ pos[2] == ':') {
+ parse_hex_byte(&pos, &delay);
+ parse_colon(&pos);
+ if (delay > 1)
+ return false;
+ }
+
+ if (!parse_caps(pos, caps, caps_len, size))
+ return false;
+
+ *delay_reporting = delay;
+
+ return true;
+}
diff --git a/profiles/audio/a2dp-helpers.h b/profiles/audio/a2dp-helpers.h
new file mode 100644
index 000000000..a5c90c516
--- /dev/null
+++ b/profiles/audio/a2dp-helpers.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef BLUEZ_A2DP_HELPERS_H
+#define BLUEZ_A2DP_HELPERS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <dbus/dbus.h>
+
+bool a2dp_parse_capabilities_array(DBusMessageIter *value,
+ uint8_t **caps, int *size);
+bool a2dp_parse_persisted_endpoint(const char *value, uint8_t *type,
+ uint8_t *codec,
+ bool *delay_reporting,
+ uint8_t *caps, size_t caps_len,
+ size_t *size);
+
+#endif /* BLUEZ_A2DP_HELPERS_H */
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index a5e002784..0999436c5 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -52,6 +52,7 @@
#include "sink.h"
#include "source.h"
#include "a2dp.h"
+#include "a2dp-helpers.h"
#include "a2dp-codecs.h"
#include "media.h"
@@ -689,7 +690,7 @@ static void stream_state_changed(struct avdtp_stream *stream,
if (err < 0 && err != -EINPROGRESS) {
error("avdtp_start: %s (%d)", strerror(-err), -err);
finalize_setup_errno(setup, err, finalize_resume,
- NULL);
+ (GSourceFunc) NULL);
return;
}
@@ -942,7 +943,8 @@ static void endpoint_open_cb(struct a2dp_setup *setup, uint8_t error_code)
if (error_code != 0) {
setup->stream = NULL;
- finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+ finalize_setup_errno(setup, -EPERM, finalize_config,
+ (GSourceFunc) NULL);
goto done;
}
@@ -954,7 +956,7 @@ static void endpoint_open_cb(struct a2dp_setup *setup, uint8_t error_code)
error("avdtp_open %s (%d)", strerror(-err), -err);
setup->stream = NULL;
- finalize_setup_errno(setup, err, finalize_config, NULL);
+ finalize_setup_errno(setup, err, finalize_config, (GSourceFunc) NULL);
done:
setup_unref(setup);
}
@@ -974,6 +976,7 @@ static void store_remote_sep(void *data, void *user_data)
char seid[4], value[256];
struct avdtp_service_capability *service = avdtp_get_codec(sep->sep);
struct avdtp_media_codec_capability *codec;
+ uint8_t delay_reporting;
unsigned int i;
ssize_t offset;
@@ -981,12 +984,13 @@ static void store_remote_sep(void *data, void *user_data)
return;
codec = (void *) service->data;
+ delay_reporting = avdtp_get_delay_reporting(sep->sep);
sprintf(seid, "%02hhx", avdtp_get_seid(sep->sep));
offset = sprintf(value, "%02hhx:%02hhx:%02hhx:",
avdtp_get_type(sep->sep), codec->media_codec_type,
- avdtp_get_delay_reporting(sep->sep));
+ delay_reporting);
for (i = 0; i < service->length - sizeof(*codec); i++)
offset += sprintf(value + offset, "%02hhx", codec->data[i]);
@@ -1139,7 +1143,8 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
return;
setup->stream = NULL;
- finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+ finalize_setup_errno(setup, -EPERM, finalize_config,
+ (GSourceFunc) NULL);
setup_unref(setup);
return;
}
@@ -1148,7 +1153,8 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (ret < 0) {
error("avdtp_open %s (%d)", strerror(-ret), -ret);
setup->stream = NULL;
- finalize_setup_errno(setup, ret, finalize_config, NULL);
+ finalize_setup_errno(setup, ret, finalize_config,
+ (GSourceFunc) NULL);
}
}
@@ -1431,7 +1437,8 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
if (start_err < 0 && start_err != -EINPROGRESS) {
error("avdtp_start: %s (%d)", strerror(-start_err),
-start_err);
- finalize_setup_errno(setup, start_err, finalize_resume, NULL);
+ finalize_setup_errno(setup, start_err, finalize_resume,
+ (GSourceFunc) NULL);
}
return TRUE;
@@ -1483,7 +1490,8 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
start_err = avdtp_start(session, a2dp_stream->stream);
if (start_err < 0 && start_err != -EINPROGRESS) {
error("avdtp_start: %s (%d)", strerror(-start_err), -start_err);
- finalize_setup_errno(setup, start_err, finalize_suspend, NULL);
+ finalize_setup_errno(setup, start_err, finalize_suspend,
+ (GSourceFunc) NULL);
}
}
@@ -1504,7 +1512,7 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
- finalize_resume, NULL);
+ finalize_resume, (GSourceFunc) NULL);
return TRUE;
}
@@ -1572,7 +1580,8 @@ static gboolean a2dp_reconfigure(gpointer data)
return FALSE;
failed:
- finalize_setup_errno(setup, posix_err, finalize_config, NULL);
+ finalize_setup_errno(setup, posix_err, finalize_config,
+ (GSourceFunc) NULL);
return FALSE;
}
@@ -1648,8 +1657,8 @@ static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return;
finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
- finalize_resume,
- finalize_config, NULL);
+ finalize_resume, finalize_config,
+ (GSourceFunc) NULL);
return;
}
@@ -1901,7 +1910,8 @@ static void channel_free(void *data)
setup->chan = NULL;
setup_ref(setup);
/* Finalize pending commands before we NULL setup->session */
- finalize_setup_errno(setup, -ENOTCONN, finalize_all, NULL);
+ finalize_setup_errno(setup, -ENOTCONN, finalize_all,
+ (GSourceFunc) NULL);
avdtp_unref(setup->session);
setup->session = NULL;
setup_unref(setup);
@@ -1991,10 +2001,12 @@ static struct a2dp_sep *find_sep(struct a2dp_server *server, uint8_t type,
static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
{
+ *caps = NULL;
+ *size = 0;
+
while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
const char *key;
DBusMessageIter value, entry;
- int var;
dbus_message_iter_recurse(props, &entry);
dbus_message_iter_get_basic(&entry, &key);
@@ -2002,15 +2014,10 @@ static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &value);
- var = dbus_message_iter_get_arg_type(&value);
if (strcasecmp(key, "Capabilities") == 0) {
- DBusMessageIter array;
-
- if (var != DBUS_TYPE_ARRAY)
+ if (!a2dp_parse_capabilities_array(&value, caps, size))
return -EINVAL;
- dbus_message_iter_recurse(&value, &array);
- dbus_message_iter_get_fixed_array(&array, caps, size);
return 0;
}
@@ -2130,7 +2137,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
struct avdtp_media_codec_capability *codec;
DBusMessageIter args, props;
const char *sender, *path;
- uint8_t *caps;
+ uint8_t *caps = NULL;
int err, size = 0;
sender = dbus_message_get_sender(msg);
@@ -2371,11 +2378,10 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
for (; *seids; seids++) {
uint8_t type;
uint8_t codec;
- uint8_t delay_reporting;
+ bool delay_reporting;
GSList *l = NULL;
- char caps[256];
uint8_t data[128];
- int i, size;
+ size_t size;
if (sscanf(*seids, "%02hhx", &rseid) != 1)
continue;
@@ -2385,34 +2391,16 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
if (!value)
continue;
- /* Try loading with delay_reporting first */
- if (sscanf(value, "%02hhx:%02hhx:%02hhx:%s", &type, &codec,
- &delay_reporting, caps) != 4) {
- /* Try old format */
- if (sscanf(value, "%02hhx:%02hhx:%s", &type, &codec,
- caps) != 3) {
- warn("Unable to load Endpoint: seid %u", rseid);
- g_free(value);
- continue;
- }
- delay_reporting = false;
- }
-
- for (i = 0, size = strlen(caps); i < size; i += 2) {
- uint8_t *tmp = data + i / 2;
-
- if (sscanf(caps + i, "%02hhx", tmp) != 1) {
- warn("Unable to load Endpoint: seid %u", rseid);
- break;
- }
+ if (!a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting, data,
+ sizeof(data), &size)) {
+ warn("Unable to load Endpoint: seid %u", rseid);
+ g_free(value);
+ continue;
}
-
g_free(value);
- if (i != size)
- continue;
-
- caps_add_codec(&l, codec, data, size / 2);
+ caps_add_codec(&l, codec, data, size);
rsep = avdtp_register_remote_sep(chan->session, rseid, type, l,
delay_reporting);
diff --git a/unit/test-a2dp.c b/unit/test-a2dp.c
new file mode 100644
index 000000000..2f7bd7bbc
--- /dev/null
+++ b/unit/test-a2dp.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "profiles/audio/a2dp-helpers.h"
+
+static DBusMessage *new_method_call(void)
+{
+ return dbus_message_new_method_call("org.bluez.test",
+ "/org/bluez/test",
+ "org.bluez.test",
+ "Test");
+}
+
+static void append_byte_array(DBusMessage *msg, const uint8_t *data, int size)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array;
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &data, size);
+ dbus_message_iter_close_container(&iter, &array);
+}
+
+static void append_string_array(DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array;
+ const char *value = "not-bytes";
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array);
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(&iter, &array);
+}
+
+static void test_capabilities_array_accepts_byte_array(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ const uint8_t bytes[] = { 0x11, 0x22, 0x33 };
+ uint8_t *caps = NULL;
+ int size = 0;
+
+ g_assert_nonnull(msg);
+
+ append_byte_array(msg, bytes, sizeof(bytes));
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_true(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_cmpint(size, ==, 3);
+ g_assert_nonnull(caps);
+ g_assert_cmpint(memcmp(caps, bytes, sizeof(bytes)), ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void test_capabilities_array_rejects_wrong_element_type(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_nonnull(msg);
+
+ append_string_array(msg);
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_false(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_null(caps);
+ g_assert_cmpint(size, ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void test_capabilities_array_rejects_empty_array(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_nonnull(msg);
+
+ append_byte_array(msg, NULL, 0);
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_false(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_cmpint(size, ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void test_capabilities_array_rejects_missing_iter(void)
+{
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_false(a2dp_parse_capabilities_array(NULL, &caps, &size));
+}
+
+static void test_capabilities_array_rejects_non_array(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ const char *value = "not-array";
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_nonnull(msg);
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &value);
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_false(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_null(caps);
+ g_assert_cmpint(size, ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void assert_endpoint(const char *value, uint8_t expected_type,
+ uint8_t expected_codec,
+ bool expected_delay,
+ const uint8_t *expected_caps,
+ size_t expected_size)
+{
+ uint8_t type = 0xff;
+ uint8_t codec = 0xff;
+ bool delay_reporting = true;
+ uint8_t caps[128];
+ size_t size = 0;
+
+ memset(caps, 0xa5, sizeof(caps));
+
+ g_assert_true(a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting,
+ caps, sizeof(caps),
+ &size));
+ g_assert_cmpint(type, ==, expected_type);
+ g_assert_cmpint(codec, ==, expected_codec);
+ g_assert_cmpint(delay_reporting, ==, expected_delay);
+ g_assert_cmpuint(size, ==, expected_size);
+ g_assert_cmpint(memcmp(caps, expected_caps, expected_size), ==, 0);
+}
+
+static void test_endpoint_parser_accepts_current_format(void)
+{
+ const uint8_t caps[] = { 0x11, 0x22, 0x33 };
+
+ assert_endpoint("00:40:01:112233", 0x00, 0x40, true, caps,
+ sizeof(caps));
+}
+
+static void test_endpoint_parser_accepts_old_format(void)
+{
+ const uint8_t caps[] = { 0xaa, 0xbb };
+
+ assert_endpoint("01:02:aabb", 0x01, 0x02, false, caps, sizeof(caps));
+}
+
+static void assert_endpoint_rejected(const char *value)
+{
+ uint8_t type = 0xff;
+ uint8_t codec = 0xff;
+ bool delay_reporting = true;
+ uint8_t caps[4] = { 0xa5, 0xa5, 0xa5, 0xa5 };
+ size_t size = 7;
+
+ g_assert_false(a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting,
+ caps, sizeof(caps),
+ &size));
+ g_assert_cmpint(caps[0], ==, 0xa5);
+ g_assert_cmpint(caps[1], ==, 0xa5);
+ g_assert_cmpint(caps[2], ==, 0xa5);
+ g_assert_cmpint(caps[3], ==, 0xa5);
+}
+
+static void test_endpoint_parser_rejects_invalid_fields(void)
+{
+ assert_endpoint_rejected(NULL);
+ assert_endpoint_rejected("");
+ assert_endpoint_rejected("00:40");
+ assert_endpoint_rejected("00:40:");
+ assert_endpoint_rejected("00:40:01:");
+ assert_endpoint_rejected("00:40:02:aabb");
+ assert_endpoint_rejected("00:40:01:aab");
+ assert_endpoint_rejected("00:40:01:aazz");
+ assert_endpoint_rejected("00:40:01:aa:bb");
+ assert_endpoint_rejected("00:40:aabb:");
+ assert_endpoint_rejected("xx:40:01:aabb");
+}
+
+static void test_endpoint_parser_rejects_missing_output_buffer(void)
+{
+ uint8_t type;
+ uint8_t codec;
+ bool delay_reporting;
+ size_t size;
+
+ g_assert_false(a2dp_parse_persisted_endpoint("00:40:01:aabb",
+ &type, &codec,
+ &delay_reporting,
+ NULL, 0, &size));
+}
+
+static void test_endpoint_parser_rejects_oversized_caps(void)
+{
+ char value[sizeof("00:40:01:") + 16];
+
+ memset(value, 'a', sizeof(value));
+ memcpy(value, "00:40:01:", strlen("00:40:01:"));
+ value[sizeof(value) - 1] = '\0';
+
+ assert_endpoint_rejected(value);
+}
+
+static void test_endpoint_parser_fuzz_cases_keep_bounds(void)
+{
+ static const char alphabet[] = "0123456789abcdefABCDEF:gZ";
+ unsigned int i;
+
+ for (i = 0; i < 4096; i++) {
+ char value[16];
+ uint8_t type;
+ uint8_t codec;
+ bool delay_reporting;
+ uint8_t caps[6] = { 0, 0, 0, 0, 0xcc, 0xdd };
+ size_t size;
+ size_t len = i % (sizeof(value) - 1);
+ size_t j;
+
+ for (j = 0; j < len; j++)
+ value[j] = alphabet[(i + j * 7) %
+ (sizeof(alphabet) - 1)];
+ value[len] = '\0';
+
+ a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting, caps, 4,
+ &size);
+ g_assert_cmpint(caps[4], ==, 0xcc);
+ g_assert_cmpint(caps[5], ==, 0xdd);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/a2dp/capabilities/byte-array",
+ test_capabilities_array_accepts_byte_array);
+ g_test_add_func("/a2dp/capabilities/wrong-element-type",
+ test_capabilities_array_rejects_wrong_element_type);
+ g_test_add_func("/a2dp/capabilities/empty-array",
+ test_capabilities_array_rejects_empty_array);
+ g_test_add_func("/a2dp/capabilities/missing-iter",
+ test_capabilities_array_rejects_missing_iter);
+ g_test_add_func("/a2dp/capabilities/non-array",
+ test_capabilities_array_rejects_non_array);
+ g_test_add_func("/a2dp/endpoint/current-format",
+ test_endpoint_parser_accepts_current_format);
+ g_test_add_func("/a2dp/endpoint/old-format",
+ test_endpoint_parser_accepts_old_format);
+ g_test_add_func("/a2dp/endpoint/invalid-fields",
+ test_endpoint_parser_rejects_invalid_fields);
+ g_test_add_func("/a2dp/endpoint/missing-output-buffer",
+ test_endpoint_parser_rejects_missing_output_buffer);
+ g_test_add_func("/a2dp/endpoint/oversized-caps",
+ test_endpoint_parser_rejects_oversized_caps);
+ g_test_add_func("/a2dp/endpoint/fuzz-bounds",
+ test_endpoint_parser_fuzz_cases_keep_bounds);
+
+ return g_test_run();
+}
--
2.43.0
^ permalink raw reply related
* [PATCH BlueZ 0/2] audio: harden A2DP parser handling
From: Geraldo Netto @ 2026-06-20 19:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Geraldo Netto
This series hardens the A2DP persisted endpoint parsing path and adds
focused unit coverage around the parser behavior. It also splits the
parser helpers out of a2dp.c to keep the endpoint parsing code easier to
test and review.
These are my first BlueZ patches, so feedback on style, structure, or
submission format is welcome.
Geraldo Netto (2):
audio: harden a2dp parsers
audio: reduce a2dp parser complexity
Makefile.am | 7 +
Makefile.plugins | 1 +
profiles/audio/a2dp-helpers.c | 167 ++++++++++++++++++++
profiles/audio/a2dp-helpers.h | 20 +++
profiles/audio/a2dp.c | 86 +++++-----
unit/test-a2dp.c | 288 ++++++++++++++++++++++++++++++++++
6 files changed, 520 insertions(+), 49 deletions(-)
create mode 100644 profiles/audio/a2dp-helpers.c
create mode 100644 profiles/audio/a2dp-helpers.h
create mode 100644 unit/test-a2dp.c
--
2.43.0
^ permalink raw reply
* Re: [syzbot] [bluetooth?] KASAN: slab-use-after-free Write in bt_accept_dequeue
From: Hillf Danton @ 2026-06-20 11:20 UTC (permalink / raw)
To: Safa Karakuş
Cc: syzbot, linux-bluetooth, linux-kernel, luiz.dentz, marcel,
syzkaller-bugs
In-Reply-To: <6a35e17e.6813c476.3c3d96.0001.GAE@google.com>
> Date: Fri, 19 Jun 2026 17:40:30 -0700
> Hello,
>
> syzbot found the following issue on:
>
> HEAD commit: 8c13415c8a43 Merge tag 'media/v7.2-1' of git://git.kernel...
> git tree: upstream
> console output: https://syzkaller.appspot.com/x/log.txt?x=12f94d56580000
> kernel config: https://syzkaller.appspot.com/x/.config?x=6a86d80fdec25236
> dashboard link: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc
> compiler: gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
>
> Unfortunately, I don't have any reproducer for this issue yet.
>
> Downloadable assets:
> disk image: https://storage.googleapis.com/syzbot-assets/3ecc105c1b32/disk-8c13415c.raw.xz
> vmlinux: https://storage.googleapis.com/syzbot-assets/cda6390db722/vmlinux-8c13415c.xz
> kernel image: https://storage.googleapis.com/syzbot-assets/5f3d326d7d36/bzImage-8c13415c.xz
>
> IMPORTANT: if you fix the issue, please add the following tag to the commit:
> Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com
>
> ==================================================================
> BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
> BUG: KASAN: slab-use-after-free in atomic_fetch_add_relaxed include/linux/atomic/atomic-instrumented.h:252 [inline]
> BUG: KASAN: slab-use-after-free in __refcount_add include/linux/refcount.h:283 [inline]
> BUG: KASAN: slab-use-after-free in __refcount_inc include/linux/refcount.h:366 [inline]
> BUG: KASAN: slab-use-after-free in refcount_inc include/linux/refcount.h:383 [inline]
> BUG: KASAN: slab-use-after-free in sock_hold include/net/sock.h:839 [inline]
> BUG: KASAN: slab-use-after-free in bt_accept_dequeue+0x6bd/0x920 net/bluetooth/af_bluetooth.c:348
> Write of size 4 at addr ffff888036a17080 by task syz.2.3018/19569
>
> CPU: 0 UID: 0 PID: 19569 Comm: syz.2.3018 Tainted: G L syzkaller #0 PREEMPT(full)
> Tainted: [L]=SOFTLOCKUP
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
> Call Trace:
> <TASK>
> __dump_stack lib/dump_stack.c:94 [inline]
> dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
> print_address_description mm/kasan/report.c:378 [inline]
> print_report+0x13d/0x4b0 mm/kasan/report.c:482
> kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
> check_region_inline mm/kasan/generic.c:186 [inline]
> kasan_check_range+0x10f/0x1e0 mm/kasan/generic.c:200
> instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
> atomic_fetch_add_relaxed include/linux/atomic/atomic-instrumented.h:252 [inline]
> __refcount_add include/linux/refcount.h:283 [inline]
> __refcount_inc include/linux/refcount.h:366 [inline]
> refcount_inc include/linux/refcount.h:383 [inline]
> sock_hold include/net/sock.h:839 [inline]
> bt_accept_dequeue+0x6bd/0x920 net/bluetooth/af_bluetooth.c:348
Sounds like ab1513597c6c ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen()
vs l2cap_conn_del()") fails to fix the root cause.
> l2cap_sock_cleanup_listen+0x47/0x4d0 net/bluetooth/l2cap_sock.c:1517
> l2cap_sock_release+0x69/0x280 net/bluetooth/l2cap_sock.c:1466
> __sock_release+0xb3/0x260 net/socket.c:710
> sock_close+0x1c/0x30 net/socket.c:1501
> __fput+0x3ff/0xb50 fs/file_table.c:512
> task_work_run+0x150/0x240 kernel/task_work.c:233
> get_signal+0x1bd/0x21e0 kernel/signal.c:2810
> arch_do_signal_or_restart+0x91/0x7e0 arch/x86/kernel/signal.c:337
> __exit_to_user_mode_loop kernel/entry/common.c:66 [inline]
> exit_to_user_mode_loop+0x139/0x6f0 kernel/entry/common.c:101
> __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
> syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
> syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
> do_syscall_64+0x666/0x870 arch/x86/entry/syscall_64.c:100
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7fc6feb9ce59
> Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
> RSP: 002b:00007fc6ff9a4028 EFLAGS: 00000246 ORIG_RAX: 0000000000000120
> RAX: fffffffffffffe00 RBX: 00007fc6fee15fa0 RCX: 00007fc6feb9ce59
> RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000005
> RBP: 00007fc6fec32e6f R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000800 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007fc6fee16038 R14: 00007fc6fee15fa0 R15: 00007ffdf85f63a8
> </TASK>
>
> Allocated by task 19587:
> kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
> kasan_save_track+0x14/0x30 mm/kasan/common.c:78
> poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
> __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
> kasan_kmalloc include/linux/kasan.h:263 [inline]
> __do_kmalloc_node mm/slub.c:5334 [inline]
> __kmalloc_noprof+0x302/0x840 mm/slub.c:5347
> _kmalloc_noprof include/linux/slab.h:973 [inline]
> sk_prot_alloc+0x10b/0x2a0 net/core/sock.c:2252
> sk_alloc+0x36/0xe80 net/core/sock.c:2308
> bt_sock_alloc+0x3b/0x3c0 net/bluetooth/af_bluetooth.c:148
> l2cap_sock_alloc.constprop.0+0x33/0x1e0 net/bluetooth/l2cap_sock.c:1983
> l2cap_sock_new_connection_cb+0x10f/0x260 net/bluetooth/l2cap_sock.c:1562
> l2cap_connect_cfm+0x4e2/0xf80 net/bluetooth/l2cap_core.c:7481
> hci_connect_cfm include/net/bluetooth/hci_core.h:2136 [inline]
> hci_remote_features_evt+0x4f4/0x9b0 net/bluetooth/hci_event.c:3764
> hci_event_func net/bluetooth/hci_event.c:7800 [inline]
> hci_event_packet+0x8e9/0xcd0 net/bluetooth/hci_event.c:7851
> hci_rx_work+0x451/0xfc0 net/bluetooth/hci_core.c:4039
> process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
> process_scheduled_works kernel/workqueue.c:3405 [inline]
> worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
> kthread+0x370/0x450 kernel/kthread.c:436
> ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
> ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
>
> Freed by task 19569:
> kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
> kasan_save_track+0x14/0x30 mm/kasan/common.c:78
> kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584
> poison_slab_object mm/kasan/common.c:253 [inline]
> __kasan_slab_free+0x5f/0x80 mm/kasan/common.c:285
> kasan_slab_free include/linux/kasan.h:235 [inline]
> slab_free_hook mm/slub.c:2700 [inline]
> slab_free mm/slub.c:6310 [inline]
> kfree+0x22b/0x6c0 mm/slub.c:6625
> sk_prot_free net/core/sock.c:2291 [inline]
> __sk_destruct+0x88c/0xab0 net/core/sock.c:2391
> sk_destruct+0xc8/0xf0 net/core/sock.c:2419
> __sk_free+0xf4/0x3e0 net/core/sock.c:2430
> sk_free+0x61/0x90 net/core/sock.c:2441
> sock_put include/net/sock.h:2020 [inline]
> bt_accept_unlink+0x22a/0x370 net/bluetooth/af_bluetooth.c:267
> bt_accept_dequeue+0x791/0x920 net/bluetooth/af_bluetooth.c:336
> l2cap_sock_cleanup_listen+0x47/0x4d0 net/bluetooth/l2cap_sock.c:1517
> l2cap_sock_release+0x69/0x280 net/bluetooth/l2cap_sock.c:1466
> __sock_release+0xb3/0x260 net/socket.c:710
> sock_close+0x1c/0x30 net/socket.c:1501
> __fput+0x3ff/0xb50 fs/file_table.c:512
> task_work_run+0x150/0x240 kernel/task_work.c:233
> get_signal+0x1bd/0x21e0 kernel/signal.c:2810
> arch_do_signal_or_restart+0x91/0x7e0 arch/x86/kernel/signal.c:337
> __exit_to_user_mode_loop kernel/entry/common.c:66 [inline]
> exit_to_user_mode_loop+0x139/0x6f0 kernel/entry/common.c:101
> __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
> syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
> syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
> do_syscall_64+0x666/0x870 arch/x86/entry/syscall_64.c:100
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> The buggy address belongs to the object at ffff888036a17000
> which belongs to the cache kmalloc-2k of size 2048
> The buggy address is located 128 bytes inside of
> freed 2048-byte region [ffff888036a17000, ffff888036a17800)
>
> The buggy address belongs to the physical page:
> page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888036a16000 pfn:0x36a10
> head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
> flags: 0xfff00000000240(workingset|head|node=0|zone=1|lastcpupid=0x7ff)
> page_type: f5(slab)
> raw: 00fff00000000240 ffff88813fe3b000 ffffea000132c210 ffffea00009e2810
> raw: ffff888036a16000 0000000800080005 00000000f5000000 0000000000000000
> head: 00fff00000000240 ffff88813fe3b000 ffffea000132c210 ffffea00009e2810
> head: ffff888036a16000 0000000800080005 00000000f5000000 0000000000000000
> head: 00fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
> head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
> page dumped because: kasan: bad access detected
> page_owner tracks the page as allocated
> page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 10, tgid 10 (kworker/0:1), ts 48507424327, free_ts 27518573283
> set_page_owner include/linux/page_owner.h:32 [inline]
> post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1853
> prep_new_page mm/page_alloc.c:1861 [inline]
> get_page_from_freelist+0x812/0x3d20 mm/page_alloc.c:3941
> __alloc_frozen_pages_noprof+0x27c/0x2b60 mm/page_alloc.c:5221
> alloc_slab_page mm/slub.c:3289 [inline]
> allocate_slab mm/slub.c:3404 [inline]
> new_slab+0xa2/0x670 mm/slub.c:3447
> refill_objects+0xe3/0x430 mm/slub.c:7241
> refill_sheaf mm/slub.c:2827 [inline]
> __pcs_replace_empty_main+0x375/0x660 mm/slub.c:4692
> alloc_from_pcs mm/slub.c:4790 [inline]
> slab_alloc_node mm/slub.c:4924 [inline]
> __do_kmalloc_node mm/slub.c:5333 [inline]
> __kmalloc_node_track_caller_noprof+0x6b5/0x890 mm/slub.c:5438
> kmalloc_reserve+0xe8/0x350 net/core/skbuff.c:637
> __alloc_skb+0x185/0x710 net/core/skbuff.c:715
> alloc_skb include/linux/skbuff.h:1386 [inline]
> mld_newpack.isra.0+0x18e/0xa20 net/ipv6/mcast.c:1773
> add_grhead+0x299/0x340 net/ipv6/mcast.c:1884
> add_grec+0x1389/0x1930 net/ipv6/mcast.c:2023
> mld_send_cr net/ipv6/mcast.c:2148 [inline]
> mld_ifc_work+0x3c5/0xc10 net/ipv6/mcast.c:2694
> process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
> process_scheduled_works kernel/workqueue.c:3405 [inline]
> worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
> kthread+0x370/0x450 kernel/kthread.c:436
> page last free pid 5380 tgid 5380 stack trace:
> reset_page_owner include/linux/page_owner.h:25 [inline]
> __free_pages_prepare mm/page_alloc.c:1397 [inline]
> __free_frozen_pages+0x794/0x10a0 mm/page_alloc.c:2938
> qlink_free mm/kasan/quarantine.c:163 [inline]
> qlist_free_all+0x47/0xf0 mm/kasan/quarantine.c:179
> kasan_quarantine_reduce+0x1a0/0x1f0 mm/kasan/quarantine.c:286
> __kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:350
> kasan_slab_alloc include/linux/kasan.h:253 [inline]
> slab_post_alloc_hook mm/slub.c:4610 [inline]
> slab_alloc_node mm/slub.c:4939 [inline]
> kmem_cache_alloc_noprof+0x241/0x6d0 mm/slub.c:4946
> vm_area_alloc+0x1f/0x160 mm/vma_init.c:32
> __mmap_new_vma mm/vma.c:2547 [inline]
> __mmap_region+0x104d/0x2dd0 mm/vma.c:2771
> mmap_region+0x35d/0x620 mm/vma.c:2857
> do_mmap+0xc63/0x12f0 mm/mmap.c:560
> vm_mmap_pgoff+0x29e/0x470 mm/util.c:581
> ksys_mmap_pgoff+0xe4/0x610 mm/mmap.c:606
> __do_sys_mmap arch/x86/kernel/sys_x86_64.c:89 [inline]
> __se_sys_mmap arch/x86/kernel/sys_x86_64.c:82 [inline]
> __x64_sys_mmap+0x125/0x190 arch/x86/kernel/sys_x86_64.c:82
> do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
> do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Memory state around the buggy address:
> ffff888036a16f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> ffff888036a17000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> >ffff888036a17080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ^
> ffff888036a17100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ffff888036a17180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ==================================================================
^ permalink raw reply
* [PATCH] Bluetooth: btrtl: set error code when RTL_SEC_PROJ read fails
From: Yinhao Hu @ 2026-06-20 10:56 UTC (permalink / raw)
To: marcel
Cc: luiz.dentz, linux-bluetooth, dzm91, hust-os-kernel-patches,
Yinhao Hu
btrtl_initialize() returns ERR_PTR(ret) at the err_free label, so every
path that jumps there must leave a negative error code in ret.
The RTL_SEC_PROJ register read stored its result in a separate variable
rc and jumped to err_free on failure without updating ret. At that point
ret is still 0 from the previous successful read, so btrtl_initialize()
returns ERR_PTR(0), i.e. NULL. btrtl_setup_realtek() only checks
IS_ERR(), then passes the NULL pointer to btrtl_download_firmware(),
which dereferences it:
Oops: general protection fault
RIP: btrtl_download_firmware+0x39
btrtl_setup_realtek
btusb_setup_realtek
hci_dev_open_sync
Read the register into ret directly and drop the now-redundant rc so the
failure propagates as a negative error pointer.
Fixes: cd8dbd9ef600 ("Bluetooth: btrtl: Avoid loading the config file on security chips")
Signed-off-by: Yinhao Hu <dddddd@hust.edu.cn>
---
drivers/bluetooth/btrtl.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 62f9d4df3a4f..eb6fdf8592c2 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -1073,7 +1073,6 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
u16 hci_rev, lmp_subver;
u8 hci_ver, lmp_ver, chip_type = 0;
int ret;
- int rc;
u8 key_id;
u8 reg_val[2];
@@ -1185,8 +1184,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
goto err_free;
}
- rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
- if (rc < 0)
+ ret = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
+ if (ret < 0)
goto err_free;
key_id = reg_val[0];
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox