iw添加wlan0导致crash问题分析
比如通过日下命令,创建一个wlan0接口
iw phy phy0 interface add wlan0 type managed
会产生如下panic内容
<1> [54245.466372] Unable to handle kernel NULL pointer dereference at virtual address 00000010
<1> [54245.474729] pgd = c1794000
<1> [54245.477443] [00000010] *pgd=01090831, *pte=00000000, *ppte=00000000
<0> [54245.483879] Internal error: Oops: 17 [#1] PREEMPT THUMB2
<4> [54245.638361] CPU: 0 PID: 21542 Comm: iw Not tainted 3.10.33 #2
<4> [54245.644095] task: c1348000 ti: c13e2000 task.ti: c13e2000
<4> [54245.649524] PC is at nl80211_send_iface+0xc/0x160
<4> [54245.654221] LR is at nl80211_new_interface+0x1cf/0x224
<4> [54245.659345] pc : [<c02956ec>] lr : [<c029a56f>] psr: 40000033
sp : c13e3c40 ip : 0000005d fp : 00000000
<4> [54245.670813] r10: 00000014 r9 : c13e3ca0 r8 : c0923100
<4> [54245.676028] r7 : 00000002 r6 : c17b8000 r5 : 00000000 r4 : c0923100
<4> [54245.682555] r3 : 00000000 r2 : 673be4b6 r1 : 00005426 r0 : c0923100
<4> [54245.689082] Flags: nZcv IRQs on FIQs on Mode SVC_32 ISA Thumb Segment user
<4> [54245.696372] Control: 50c53c7d Table: 01794059 DAC: 00000015
nl80211_send_iface的wdev这个参数为空指针 ;看看nl80211_new_interface调用nl80211_send_iface之前,是怎么给wdev赋值的,如下述代码是从rdev_add_virtual_intf获取wdev这个wireless_dev结构体;也就是要找到这个函数为什么返回空指针
static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct vif_params params;
struct wireless_dev *wdev;
struct sk_buff *msg;
int err;
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
/* to avoid failing a new interface creation due to pending removal */
cfg80211_destroy_ifaces(rdev);
memset(¶ms, 0, sizeof(params));
if (!info->attrs[NL80211_ATTR_IFNAME])
return -EINVAL;
if (info->attrs[NL80211_ATTR_IFTYPE]) {
type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
if (type > NL80211_IFTYPE_MAX)
return -EINVAL;
}
if (!rdev->ops->add_virtual_intf ||
!(rdev->wiphy.interface_modes & (1 << type)))
return -EOPNOTSUPP;
if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN ||
rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
info->attrs[NL80211_ATTR_MAC]) {
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
ETH_ALEN);
if (!is_valid_ether_addr(params.macaddr))
return -EADDRNOTAVAIL;
}
if (info->attrs[NL80211_ATTR_4ADDR]) {
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
if (err)
return err;
}
err = nl80211_parse_mon_options(rdev, type, info, ¶ms);
if (err < 0)
return err;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
wdev = rdev_add_virtual_intf(rdev,
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
NET_NAME_USER, type, ¶ms);
if (WARN_ON(!wdev)) {
nlmsg_free(msg);
return -EPROTO;
} else if (IS_ERR(wdev)) {
nlmsg_free(msg);
return PTR_ERR(wdev);
}
if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
wdev->owner_nlportid = info->snd_portid;
switch (type) {
case NL80211_IFTYPE_MESH_POINT:
if (!info->attrs[NL80211_ATTR_MESH_ID])
break;
wdev_lock(wdev);
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
IEEE80211_MAX_MESH_ID_LEN);
wdev->mesh_id_up_len =
nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
wdev->mesh_id_up_len);
wdev_unlock(wdev);
break;
case NL80211_IFTYPE_NAN:
case NL80211_IFTYPE_P2P_DEVICE:
/*
* P2P Device and NAN do not have a netdev, so don't go
* through the netdev notifier and must be added here
*/
mutex_init(&wdev->mtx);
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
rdev->devlist_generation++;
break;
default:
break;
}
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
rdev, wdev, false) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
/*
* For wdevs which have no associated netdev object (e.g. of type
* NL80211_IFTYPE_P2P_DEVICE), emit the NEW_INTERFACE event here.
* For all other types, the event will be generated from the
* netdev notifier
*/
if (!wdev->netdev)
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
return genlmsg_reply(msg, info);
}
rdev_add_virtual_intf调用的是cfg80211_registered_device的ops成员的add_virtual_intf函数指针
static inline struct wireless_dev
*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
struct vif_params *params)
{
struct wireless_dev *ret;
trace_rdev_add_virtual_intf(&rdev->wiphy, name, type);
ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type,
type, params);
trace_rdev_return_wdev(&rdev->wiphy, ret);
return ret;
}
wifi驱动会调用wiphy_new来分配一个cfg80211_registered_device,并且将cfg80211_ops赋值给这个cfg80211_registered_device的ops成员
struct cfg80211_ops wifi_cfg80211_ops = {
.add_virtual_intf = wifi_interface_add,
.del_virtual_intf = wifi_interface_del
.....
}
wiphy_new(&ssv_cfg80211_ops, sizeof(struct ssv_softc));
wiphy_new的实现如下:主要是申请一块cfg80211_registered_device的内存,并将第一个ops参数赋值给ops成员
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
const char *requested_name)
{
....
struct cfg80211_registered_device *rdev;
rdev = kzalloc(alloc_size, GFP_KERNEL);
rdev->ops = ops;
wiphy_net_set(&rdev->wiphy, &init_net);
....
}
解决办法如下:通过__dev_get_by_name从网络命令空间init_net查找wlan0这个net_device是否已经被注册到系统,检测到直接返回wireless_dev;不然直接再通过register_netdevice注册一个wlan0,就返回NULL了
--- a/package/kernel/wifi/src/fmac/netdev_ops.c
+++ b/package/kernel/wifi/src/fmac/netdev_ops.c
@@ -498,6 +498,10 @@ struct wireless_dev *wifi_interface_add(struct ssv_softc *sc,
struct net_device *ndev;
int min_idx, max_idx;
int vif_idx = -1;
int i;
+
+ ndev = __dev_get_by_name(&init_net, "wlan0");
+ if (ndev)
+ return ndev->ieee80211_ptr;
ndev = alloc_netdev(sizeof(*vif), name, ssv_netdev_setup);
if (!ndev)
return NULL;
....
if (register_netdevice(ndev))
return NULL;
原文地址:https://blog.csdn.net/weixin_44586903/article/details/143902433
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!