Skip to content

Commit b590b9a

Browse files
committed
wifi: cfg80211: add locked debugfs wrappers
Add wrappers for debugfs files that should be called with the wiphy mutex held, while the file is also to be removed under the wiphy mutex. This could otherwise deadlock when a file is trying to acquire the wiphy mutex while the code removing it holds the mutex but waits for the removal. This actually works by pushing the execution of the read or write handler to a wiphy work that can be cancelled using the debugfs cancellation API. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent 8c88a47 commit b590b9a

2 files changed

Lines changed: 206 additions & 0 deletions

File tree

include/net/cfg80211.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9299,4 +9299,50 @@ bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
92999299
*/
93009300
void cfg80211_links_removed(struct net_device *dev, u16 link_mask);
93019301

9302+
#ifdef CONFIG_CFG80211_DEBUGFS
9303+
/**
9304+
* wiphy_locked_debugfs_read - do a locked read in debugfs
9305+
* @wiphy: the wiphy to use
9306+
* @file: the file being read
9307+
* @buf: the buffer to fill and then read from
9308+
* @bufsize: size of the buffer
9309+
* @userbuf: the user buffer to copy to
9310+
* @count: read count
9311+
* @ppos: read position
9312+
* @handler: the read handler to call (under wiphy lock)
9313+
* @data: additional data to pass to the read handler
9314+
*/
9315+
ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file,
9316+
char *buf, size_t bufsize,
9317+
char __user *userbuf, size_t count,
9318+
loff_t *ppos,
9319+
ssize_t (*handler)(struct wiphy *wiphy,
9320+
struct file *file,
9321+
char *buf,
9322+
size_t bufsize,
9323+
void *data),
9324+
void *data);
9325+
9326+
/**
9327+
* wiphy_locked_debugfs_write - do a locked write in debugfs
9328+
* @wiphy: the wiphy to use
9329+
* @file: the file being written to
9330+
* @buf: the buffer to copy the user data to
9331+
* @bufsize: size of the buffer
9332+
* @userbuf: the user buffer to copy from
9333+
* @count: read count
9334+
* @handler: the write handler to call (under wiphy lock)
9335+
* @data: additional data to pass to the write handler
9336+
*/
9337+
ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy, struct file *file,
9338+
char *buf, size_t bufsize,
9339+
const char __user *userbuf, size_t count,
9340+
ssize_t (*handler)(struct wiphy *wiphy,
9341+
struct file *file,
9342+
char *buf,
9343+
size_t count,
9344+
void *data),
9345+
void *data);
9346+
#endif
9347+
93029348
#endif /* __NET_CFG80211_H */

net/wireless/debugfs.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*
55
* Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
66
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
7+
* Copyright (C) 2023 Intel Corporation
78
*/
89

910
#include <linux/slab.h>
@@ -109,3 +110,162 @@ void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
109110
DEBUGFS_ADD(long_retry_limit);
110111
DEBUGFS_ADD(ht40allow_map);
111112
}
113+
114+
struct debugfs_read_work {
115+
struct wiphy_work work;
116+
ssize_t (*handler)(struct wiphy *wiphy,
117+
struct file *file,
118+
char *buf,
119+
size_t count,
120+
void *data);
121+
struct wiphy *wiphy;
122+
struct file *file;
123+
char *buf;
124+
size_t bufsize;
125+
void *data;
126+
ssize_t ret;
127+
struct completion completion;
128+
};
129+
130+
static void wiphy_locked_debugfs_read_work(struct wiphy *wiphy,
131+
struct wiphy_work *work)
132+
{
133+
struct debugfs_read_work *w = container_of(work, typeof(*w), work);
134+
135+
w->ret = w->handler(w->wiphy, w->file, w->buf, w->bufsize, w->data);
136+
complete(&w->completion);
137+
}
138+
139+
static void wiphy_locked_debugfs_read_cancel(struct dentry *dentry,
140+
void *data)
141+
{
142+
struct debugfs_read_work *w = data;
143+
144+
wiphy_work_cancel(w->wiphy, &w->work);
145+
complete(&w->completion);
146+
}
147+
148+
ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file,
149+
char *buf, size_t bufsize,
150+
char __user *userbuf, size_t count,
151+
loff_t *ppos,
152+
ssize_t (*handler)(struct wiphy *wiphy,
153+
struct file *file,
154+
char *buf,
155+
size_t bufsize,
156+
void *data),
157+
void *data)
158+
{
159+
struct debugfs_read_work work = {
160+
.handler = handler,
161+
.wiphy = wiphy,
162+
.file = file,
163+
.buf = buf,
164+
.bufsize = bufsize,
165+
.data = data,
166+
.ret = -ENODEV,
167+
.completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
168+
};
169+
struct debugfs_cancellation cancellation = {
170+
.cancel = wiphy_locked_debugfs_read_cancel,
171+
.cancel_data = &work,
172+
};
173+
174+
/* don't leak stack data or whatever */
175+
memset(buf, 0, bufsize);
176+
177+
wiphy_work_init(&work.work, wiphy_locked_debugfs_read_work);
178+
wiphy_work_queue(wiphy, &work.work);
179+
180+
debugfs_enter_cancellation(file, &cancellation);
181+
wait_for_completion(&work.completion);
182+
debugfs_leave_cancellation(file, &cancellation);
183+
184+
if (work.ret < 0)
185+
return work.ret;
186+
187+
if (WARN_ON(work.ret > bufsize))
188+
return -EINVAL;
189+
190+
return simple_read_from_buffer(userbuf, count, ppos, buf, work.ret);
191+
}
192+
EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_read);
193+
194+
struct debugfs_write_work {
195+
struct wiphy_work work;
196+
ssize_t (*handler)(struct wiphy *wiphy,
197+
struct file *file,
198+
char *buf,
199+
size_t count,
200+
void *data);
201+
struct wiphy *wiphy;
202+
struct file *file;
203+
char *buf;
204+
size_t count;
205+
void *data;
206+
ssize_t ret;
207+
struct completion completion;
208+
};
209+
210+
static void wiphy_locked_debugfs_write_work(struct wiphy *wiphy,
211+
struct wiphy_work *work)
212+
{
213+
struct debugfs_write_work *w = container_of(work, typeof(*w), work);
214+
215+
w->ret = w->handler(w->wiphy, w->file, w->buf, w->count, w->data);
216+
complete(&w->completion);
217+
}
218+
219+
static void wiphy_locked_debugfs_write_cancel(struct dentry *dentry,
220+
void *data)
221+
{
222+
struct debugfs_write_work *w = data;
223+
224+
wiphy_work_cancel(w->wiphy, &w->work);
225+
complete(&w->completion);
226+
}
227+
228+
ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy,
229+
struct file *file, char *buf, size_t bufsize,
230+
const char __user *userbuf, size_t count,
231+
ssize_t (*handler)(struct wiphy *wiphy,
232+
struct file *file,
233+
char *buf,
234+
size_t count,
235+
void *data),
236+
void *data)
237+
{
238+
struct debugfs_write_work work = {
239+
.handler = handler,
240+
.wiphy = wiphy,
241+
.file = file,
242+
.buf = buf,
243+
.count = count,
244+
.data = data,
245+
.ret = -ENODEV,
246+
.completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
247+
};
248+
struct debugfs_cancellation cancellation = {
249+
.cancel = wiphy_locked_debugfs_write_cancel,
250+
.cancel_data = &work,
251+
};
252+
253+
/* mostly used for strings so enforce NUL-termination for safety */
254+
if (count >= bufsize)
255+
return -EINVAL;
256+
257+
memset(buf, 0, bufsize);
258+
259+
if (copy_from_user(buf, userbuf, count))
260+
return -EFAULT;
261+
262+
wiphy_work_init(&work.work, wiphy_locked_debugfs_write_work);
263+
wiphy_work_queue(wiphy, &work.work);
264+
265+
debugfs_enter_cancellation(file, &cancellation);
266+
wait_for_completion(&work.completion);
267+
debugfs_leave_cancellation(file, &cancellation);
268+
269+
return work.ret;
270+
}
271+
EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_write);

0 commit comments

Comments
 (0)