Skip to content

[PW_SID:1157499] [v2] Bluetooth: RFCOMM: defer security confirmation to krfcommd - #702

Open
BluezTestBot wants to merge 6 commits into
workflowfrom
1157499
Open

[PW_SID:1157499] [v2] Bluetooth: RFCOMM: defer security confirmation to krfcommd#702
BluezTestBot wants to merge 6 commits into
workflowfrom
1157499

Conversation

@BluezTestBot

Copy link
Copy Markdown

An RFCOMM connect() issued while a BR/EDR link is being authenticated
makes lockdep report a circular dependency, and the reported cycle is a
real AB/BA between rfcomm_mutex and hdev->lock.

rfcomm_security_cfm() is called from the HCI event path, which already
holds hdev->lock:

hci_rx_work()
hci_event_packet()
hci_cc_read_enc_key_size() [hdev->lock]
hci_encrypt_cfm() [hci_cb_list_lock]
rfcomm_security_cfm() [rfcomm_mutex]

while an RFCOMM connect() from userspace takes the same two locks the
other way round:

rfcomm_sock_connect()
rfcomm_dlc_open() [rfcomm_mutex]
__rfcomm_dlc_open()
rfcomm_session_create()
kernel_connect()
l2cap_sock_connect()
l2cap_chan_connect() [hdev->lock]

WARNING: possible circular locking dependency detected
kworker/u131:1/1128 is trying to acquire lock:
rfcomm_mutex, at: rfcomm_security_cfm+0x31/0x3e0 [rfcomm]
but task is already holding lock:
hci_cb_list_lock, at: hci_cc_read_enc_key_size+0x1d2/0xcc0
Chain exists of:
rfcomm_mutex --> &hdev->lock --> hci_cb_list_lock

hci_auth_complete_evt() and hci_encrypt_change_evt() reach the callback
the same way.

Both orders have to be seen in the same boot, which is why a BR/EDR
connection alone is not enough to show it: a session set up by the
remote side is created by rfcomm_accept_connection() in krfcommd, which
calls kernel_accept() and never takes hdev->lock under rfcomm_mutex.
Connecting a device that authenticates and encrypts the link and then
calling connect() on an RFCOMM socket towards any address - the connect
does not have to succeed, the order is recorded before the page timeout

  • reports it every time.

The callback does not have to run in the HCI event context at all: it
only updates DLC flags and timers that krfcommd consumes in
rfcomm_process_dlcs(), and it already ends with rfcomm_schedule(). So
queue the confirmation instead of taking rfcomm_mutex from the HCI
event path, and let krfcommd apply it under rfcomm_mutex on its next
pass, ahead of session processing.

The queued entry pins both the connection and the controller, and
krfcommd takes hdev->lock while applying it, so the lookup and
hci_conn_check_secure() run in the same context as before. A session
that was torn down and set up again while the confirmation was queued
runs over a different hci_conn and is skipped. A confirmation that
cannot be allocated is dropped and the DLC closes on its auth timeout.

Fixes: 759c185d0bbd ("Bluetooth: RFCOMM: serialize security confirmation handling")
Reported-by: Pauli Virtanen pav@iki.fi
Closes: https://lore.kernel.org/linux-bluetooth/5e76a95e934e451e7006db28827c2d64af5a88be.camel@iki.fi/
Reported-by: syzbot+74071deb72339c215b2e@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-bluetooth/6a92fadc.08e933ee.dbf97.008f.GAE@google.com/
Cc: stable@vger.kernel.org
Signed-off-by: Mikhail Gavrilov mikhail.v.gavrilov@gmail.com

The commit this fixes is in v7.3-rc1 and is marked for stable, so this
probably wants the bluetooth fixes tree rather than -next.

v1: https://lore.kernel.org/linux-bluetooth/20260902235132.453044-1-mikhail.v.gavrilov@gmail.com/

v2:

  • free queued confirmations from rfcomm_init() and rfcomm_exit() after
    hci_unregister_cb(), instead of at the end of rfcomm_run(); the init
    error path stops the thread before unregistering the callback, so
    the old placement leaked there
  • pin the controller too and take hdev->lock while the confirmation is
    applied, so conn->sec_level is read in the same context as before
  • skip a session that runs over a different hci_conn than the one the
    confirmation was reported for
  • context-analysis annotations for security_cfm_list and
    __rfcomm_security_cfm(); not verified with clang, done by inspection
  • the reproducer below now uses spaces, gitlint tripped over the tabs

Tested on 7.3.0-rc1 with an MT7922 controller (btusb). Without the
patch the steps above report the inversion on every run. With v2
applied the reproducer leaves the validator armed and silent
(debug_locks: 1), and a 2.5 hour session with three BR/EDR headsets
(soundcore Liberty 5, FIIO UTWS17, JBL Tour Pro 3), HFP/SCO audio and
AVRCP produced no lockdep report.

The connect() side used for testing, so that it does not depend on which
end sets up the HFP session:

#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>

#define BTPROTO_RFCOMM 3

struct sockaddr_rc {
unsigned short rc_family;
uint8_t rc_bdaddr[6]; /* little endian */
uint8_t rc_channel;
};

int main(void)
{
struct sockaddr_rc addr = { .rc_family = AF_BLUETOOTH,
.rc_channel = 1 };
int fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

  memcpy(addr.rc_bdaddr, "\x55\x44\x33\x22\x11\x00", 6);
  connect(fd, (struct sockaddr *)&addr, sizeof(addr));
  close(fd);
  return 0;

}

net/bluetooth/rfcomm/core.c | 178 ++++++++++++++++++++++++++++--------
1 file changed, 140 insertions(+), 38 deletions(-)

tedd-an and others added 6 commits September 3, 2026 20:57
This patch adds workflow files for ci:

[sync.yml]
 - The workflow file for scheduled work
 - Sync the repo with upstream repo and rebase the workflow branch
 - Review the patches in the patchwork and creates the PR if needed

[ci.yml]
 - The workflow file for CI tasks
 - Run CI tests when PR is created

Signed-off-by: Tedd Ho-Jeong An <tedd.an@intel.com>
This replaces the bzcafe action with bluez/action-ci so we can maintain
everything in the github bluez organization

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This attempts to sync every 5 minutes instead of 30.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
bluez/action-ci uses master as default branch for workflow which is
incorrect for kernel

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The CI action now creates individual GitHub Check Runs per test, which
requires 'checks: write' permission on the GITHUB_TOKEN. Also make the
pull_request trigger types explicit to include 'reopened', allowing CI
to be retriggered by closing and reopening a PR.
An RFCOMM connect() issued while a BR/EDR link is being authenticated
makes lockdep report a circular dependency, and the reported cycle is a
real AB/BA between rfcomm_mutex and hdev->lock.

rfcomm_security_cfm() is called from the HCI event path, which already
holds hdev->lock:

  hci_rx_work()
    hci_event_packet()
      hci_cc_read_enc_key_size()   [hdev->lock]
        hci_encrypt_cfm()          [hci_cb_list_lock]
          rfcomm_security_cfm()    [rfcomm_mutex]

while an RFCOMM connect() from userspace takes the same two locks the
other way round:

  rfcomm_sock_connect()
    rfcomm_dlc_open()              [rfcomm_mutex]
      __rfcomm_dlc_open()
        rfcomm_session_create()
          kernel_connect()
            l2cap_sock_connect()
              l2cap_chan_connect() [hdev->lock]

  WARNING: possible circular locking dependency detected
  kworker/u131:1/1128 is trying to acquire lock:
  rfcomm_mutex, at: rfcomm_security_cfm+0x31/0x3e0 [rfcomm]
  but task is already holding lock:
  hci_cb_list_lock, at: hci_cc_read_enc_key_size+0x1d2/0xcc0
  Chain exists of:
    rfcomm_mutex --> &hdev->lock --> hci_cb_list_lock

hci_auth_complete_evt() and hci_encrypt_change_evt() reach the callback
the same way.

Both orders have to be seen in the same boot, which is why a BR/EDR
connection alone is not enough to show it: a session set up by the
remote side is created by rfcomm_accept_connection() in krfcommd, which
calls kernel_accept() and never takes hdev->lock under rfcomm_mutex.
Connecting a device that authenticates and encrypts the link and then
calling connect() on an RFCOMM socket towards any address - the connect
does not have to succeed, the order is recorded before the page timeout
- reports it every time.

The callback does not have to run in the HCI event context at all: it
only updates DLC flags and timers that krfcommd consumes in
rfcomm_process_dlcs(), and it already ends with rfcomm_schedule().  So
queue the confirmation instead of taking rfcomm_mutex from the HCI
event path, and let krfcommd apply it under rfcomm_mutex on its next
pass, ahead of session processing.

The queued entry pins both the connection and the controller, and
krfcommd takes hdev->lock while applying it, so the lookup and
hci_conn_check_secure() run in the same context as before.  A session
that was torn down and set up again while the confirmation was queued
runs over a different hci_conn and is skipped.  A confirmation that
cannot be allocated is dropped and the DLC closes on its auth timeout.

Fixes: 759c185d0bbd ("Bluetooth: RFCOMM: serialize security confirmation handling")
Reported-by: Pauli Virtanen <pav@iki.fi>
Closes: https://lore.kernel.org/linux-bluetooth/5e76a95e934e451e7006db28827c2d64af5a88be.camel@iki.fi/
Reported-by: syzbot+74071deb72339c215b2e@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-bluetooth/6a92fadc.08e933ee.dbf97.008f.GAE@google.com/
Cc: stable@vger.kernel.org
Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

CheckPatch
Desc: Run checkpatch.pl script
Duration: 0.69 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

VerifyFixes
Desc: Verify Fixes tag format and validity
Duration: 0.08 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

VerifySignedoff
Desc: Verify Signed-off-by chain
Duration: 0.08 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

GitLint
Desc: Run gitlint
Duration: 0.22 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

SubjectPrefix
Desc: Check subject contains "Bluetooth" prefix
Duration: 0.08 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

BuildKernel
Desc: Build Kernel for Bluetooth
Duration: 28.08 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

CheckAllWarning
Desc: Run linux kernel with all warning enabled
Duration: 30.97 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

CheckSparse
Desc: Run sparse tool with linux kernel
Duration: 29.34 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

BuildKernel32
Desc: Build 32bit Kernel for Bluetooth
Duration: 27.13 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

CheckKernelLLVM
Desc: Build kernel with LLVM + context analysis
Duration: 0.00 seconds
Result: SKIP
Output:

Clang not found

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

TestRunnerSetup
Desc: Setup kernel and bluez for test-runner
Duration: 511.60 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

TestRunner_rfcomm-tester
Desc: Run rfcomm-tester with test-runner
Duration: 25.90 seconds
Result: PASS

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

IncrementalBuild
Desc: Incremental build with the patches in the series
Duration: 26.11 seconds
Result: PASS

@github-actions
github-actions Bot force-pushed the workflow branch 2 times, most recently from 8f53a45 to d690686 Compare September 4, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants