]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/6.6.26/bluetooth-fix-toctou-in-hci-debugfs-implementation.patch
Linux 6.6.26
[thirdparty/kernel/stable-queue.git] / releases / 6.6.26 / bluetooth-fix-toctou-in-hci-debugfs-implementation.patch
1 From 7835fcfd132eb88b87e8eb901f88436f63ab60f7 Mon Sep 17 00:00:00 2001
2 From: Bastien Nocera <hadess@hadess.net>
3 Date: Wed, 27 Mar 2024 15:24:56 +0100
4 Subject: Bluetooth: Fix TOCTOU in HCI debugfs implementation
5
6 From: Bastien Nocera <hadess@hadess.net>
7
8 commit 7835fcfd132eb88b87e8eb901f88436f63ab60f7 upstream.
9
10 struct hci_dev members conn_info_max_age, conn_info_min_age,
11 le_conn_max_interval, le_conn_min_interval, le_adv_max_interval,
12 and le_adv_min_interval can be modified from the HCI core code, as well
13 through debugfs.
14
15 The debugfs implementation, that's only available to privileged users,
16 will check for boundaries, making sure that the minimum value being set
17 is strictly above the maximum value that already exists, and vice-versa.
18
19 However, as both minimum and maximum values can be changed concurrently
20 to us modifying them, we need to make sure that the value we check is
21 the value we end up using.
22
23 For example, with ->conn_info_max_age set to 10, conn_info_min_age_set()
24 gets called from vfs handlers to set conn_info_min_age to 8.
25
26 In conn_info_min_age_set(), this goes through:
27 if (val == 0 || val > hdev->conn_info_max_age)
28 return -EINVAL;
29
30 Concurrently, conn_info_max_age_set() gets called to set to set the
31 conn_info_max_age to 7:
32 if (val == 0 || val > hdev->conn_info_max_age)
33 return -EINVAL;
34 That check will also pass because we used the old value (10) for
35 conn_info_max_age.
36
37 After those checks that both passed, the struct hci_dev access
38 is mutex-locked, disabling concurrent access, but that does not matter
39 because the invalid value checks both passed, and we'll end up with
40 conn_info_min_age = 8 and conn_info_max_age = 7
41
42 To fix this problem, we need to lock the structure access before so the
43 check and assignment are not interrupted.
44
45 This fix was originally devised by the BassCheck[1] team, and
46 considered the problem to be an atomicity one. This isn't the case as
47 there aren't any concerns about the variable changing while we check it,
48 but rather after we check it parallel to another change.
49
50 This patch fixes CVE-2024-24858 and CVE-2024-24857.
51
52 [1] https://sites.google.com/view/basscheck/
53
54 Co-developed-by: Gui-Dong Han <2045gemini@gmail.com>
55 Signed-off-by: Gui-Dong Han <2045gemini@gmail.com>
56 Link: https://lore.kernel.org/linux-bluetooth/20231222161317.6255-1-2045gemini@gmail.com/
57 Link: https://nvd.nist.gov/vuln/detail/CVE-2024-24858
58 Link: https://lore.kernel.org/linux-bluetooth/20231222162931.6553-1-2045gemini@gmail.com/
59 Link: https://lore.kernel.org/linux-bluetooth/20231222162310.6461-1-2045gemini@gmail.com/
60 Link: https://nvd.nist.gov/vuln/detail/CVE-2024-24857
61 Fixes: 31ad169148df ("Bluetooth: Add conn info lifetime parameters to debugfs")
62 Fixes: 729a1051da6f ("Bluetooth: Expose default LE advertising interval via debugfs")
63 Fixes: 71c3b60ec6d2 ("Bluetooth: Move BR/EDR debugfs file creation into hci_debugfs.c")
64 Signed-off-by: Bastien Nocera <hadess@hadess.net>
65 Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
66 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
67 ---
68 net/bluetooth/hci_debugfs.c | 48 +++++++++++++++++++++++++++++---------------
69 1 file changed, 32 insertions(+), 16 deletions(-)
70
71 --- a/net/bluetooth/hci_debugfs.c
72 +++ b/net/bluetooth/hci_debugfs.c
73 @@ -218,10 +218,12 @@ static int conn_info_min_age_set(void *d
74 {
75 struct hci_dev *hdev = data;
76
77 - if (val == 0 || val > hdev->conn_info_max_age)
78 + hci_dev_lock(hdev);
79 + if (val == 0 || val > hdev->conn_info_max_age) {
80 + hci_dev_unlock(hdev);
81 return -EINVAL;
82 + }
83
84 - hci_dev_lock(hdev);
85 hdev->conn_info_min_age = val;
86 hci_dev_unlock(hdev);
87
88 @@ -246,10 +248,12 @@ static int conn_info_max_age_set(void *d
89 {
90 struct hci_dev *hdev = data;
91
92 - if (val == 0 || val < hdev->conn_info_min_age)
93 + hci_dev_lock(hdev);
94 + if (val == 0 || val < hdev->conn_info_min_age) {
95 + hci_dev_unlock(hdev);
96 return -EINVAL;
97 + }
98
99 - hci_dev_lock(hdev);
100 hdev->conn_info_max_age = val;
101 hci_dev_unlock(hdev);
102
103 @@ -567,10 +571,12 @@ static int sniff_min_interval_set(void *
104 {
105 struct hci_dev *hdev = data;
106
107 - if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
108 + hci_dev_lock(hdev);
109 + if (val == 0 || val % 2 || val > hdev->sniff_max_interval) {
110 + hci_dev_unlock(hdev);
111 return -EINVAL;
112 + }
113
114 - hci_dev_lock(hdev);
115 hdev->sniff_min_interval = val;
116 hci_dev_unlock(hdev);
117
118 @@ -595,10 +601,12 @@ static int sniff_max_interval_set(void *
119 {
120 struct hci_dev *hdev = data;
121
122 - if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
123 + hci_dev_lock(hdev);
124 + if (val == 0 || val % 2 || val < hdev->sniff_min_interval) {
125 + hci_dev_unlock(hdev);
126 return -EINVAL;
127 + }
128
129 - hci_dev_lock(hdev);
130 hdev->sniff_max_interval = val;
131 hci_dev_unlock(hdev);
132
133 @@ -850,10 +858,12 @@ static int conn_min_interval_set(void *d
134 {
135 struct hci_dev *hdev = data;
136
137 - if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
138 + hci_dev_lock(hdev);
139 + if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval) {
140 + hci_dev_unlock(hdev);
141 return -EINVAL;
142 + }
143
144 - hci_dev_lock(hdev);
145 hdev->le_conn_min_interval = val;
146 hci_dev_unlock(hdev);
147
148 @@ -878,10 +888,12 @@ static int conn_max_interval_set(void *d
149 {
150 struct hci_dev *hdev = data;
151
152 - if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
153 + hci_dev_lock(hdev);
154 + if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval) {
155 + hci_dev_unlock(hdev);
156 return -EINVAL;
157 + }
158
159 - hci_dev_lock(hdev);
160 hdev->le_conn_max_interval = val;
161 hci_dev_unlock(hdev);
162
163 @@ -990,10 +1002,12 @@ static int adv_min_interval_set(void *da
164 {
165 struct hci_dev *hdev = data;
166
167 - if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
168 + hci_dev_lock(hdev);
169 + if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval) {
170 + hci_dev_unlock(hdev);
171 return -EINVAL;
172 + }
173
174 - hci_dev_lock(hdev);
175 hdev->le_adv_min_interval = val;
176 hci_dev_unlock(hdev);
177
178 @@ -1018,10 +1032,12 @@ static int adv_max_interval_set(void *da
179 {
180 struct hci_dev *hdev = data;
181
182 - if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
183 + hci_dev_lock(hdev);
184 + if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval) {
185 + hci_dev_unlock(hdev);
186 return -EINVAL;
187 + }
188
189 - hci_dev_lock(hdev);
190 hdev->le_adv_max_interval = val;
191 hci_dev_unlock(hdev);
192