Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

core: no defined synchronisation for `libusb_device::parent_dev`

オープン
#1,918 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
35/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
静か
技術スタック
c
領域
backend

調査の方向性

Start by reading libusbi.h and the parent_dev accesses in core.c, hotplug.c, and Darwin's process_new_device. Decide how synchronization and references should protect readers and writers, checking lock ordering and existing tests before choosing an approach. Done means concurrent device-list walks and re-enumeration no longer race or leave readers with an unreferenced parent; validate with concurrency testing such as TSan.

索引モデルが issue の本文から書いたものです。

説明

core

core: no defined synchronisation for libusb_device::parent_dev

parent_dev is a plain struct libusb_device * (libusbi.h:523) with no documented or
enforced locking rule, and the code currently makes three different assumptions about it.

Public readers take no lock. libusb_get_port_numbers (core.c) walks the chain twice,
and libusb_get_parent returns the pointer directly. Neither acquires anything.

One core path assumes ctx->usb_devs_lock. usbi_hotplug_exit walks and mutates
whole parent chains through usbi_recursively_remove_parents (hotplug.c:229) while
holding that lock (hotplug.c:219-233).

Backends write it without any lock. The darwin backend is currently the only one that
rewrites the field on a device that is already published to the application: on
re-enumeration, process_new_device resolves and stores a new parent. Linux and Windows
assign it only while constructing a fresh device.

The documented contracts differ too. libusb_get_parent tells callers to stay inside a
libusb_get_device_list() / libusb_free_device_list() block, which incidentally keeps a
reference on the parent. libusb_get_port_numbers carries no such requirement, so a caller
following the documentation can walk the chain with no reference held on anything but the
leaf device.

Evidence

TSan reports it directly when a device list walk runs concurrently with a re-enumeration,
from @mcuee's runs on #1886:

Read of size 8 at 0x…2390 by thread T2:
  #0 libusb_get_port_numbers
Previous write of size 8 at 0x…2390 by thread T1:
  #0 process_new_device
  #1 darwin_devices_attached
Location is heap block of size 120 at 0x…2380 allocated by main thread:
  #1 usbi_alloc_device

Offset 16 with size 8 is parent_dev (refcnt 0, ctx 8, parent_dev 16), and the
120-byte block matches PTR_ALIGN(sizeof(struct libusb_device)) plus darwin's 8-byte
device private data.

Candidate fixes

Make the field atomic. Smallest change, removes the C11 data race and the TSan report.
Touches libusbi.h and every backend that assigns the field. It does not stop a reader from
holding a pointer that is freed after it loads it.

Give the field a lock. ctx->usb_devs_lock is the natural candidate, since
usbi_hotplug_exit already treats it that way. This would mean libusb_get_port_numbers
acquiring it around the walk and backends acquiring it around the update. It closes the
race properly, but it introduces locking into a public accessor that has never taken one,
so it needs a look at lock ordering and at what callers may already hold.

Either way, the reader also has to hold a reference on each device it walks if the
use-after-free window is to be closed rather than narrowed.

Related

#1915 bounds the fill loop in libusb_get_port_numbers so a chain that changes length
between its two passes cannot write outside the caller's array. That removes the memory
corruption, not the race.

#1917 narrows the window on Darwin side: the new parent is
stored before the old one is released, so the field always holds either NULL or a
referenced device. A walk that already loaded the old pointer can still race.

Assisted-by: claude-code:claude-opus-5

主要言語
C
スター
6.2k
フォーク
2.1k
平均マージ
28日 9時間
マージ済み PR(30日)
1

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

libusb/libusb のほかの issue

libusb/libusb の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。