Skip to content

Commit a2e0655

Browse files
TaeheeYoogregkh
authored andcommitted
net: core: limit nested device depth
[ Upstream commit 5343da4 ] Current code doesn't limit the number of nested devices. Nested devices would be handled recursively and this needs huge stack memory. So, unlimited nested devices could make stack overflow. This patch adds upper_level and lower_level, they are common variables and represent maximum lower/upper depth. When upper/lower device is attached or dettached, {lower/upper}_level are updated. and if maximum depth is bigger than 8, attach routine fails and returns -EMLINK. In addition, this patch converts recursive routine of netdev_walk_all_{lower/upper} to iterator routine. Test commands: ip link add dummy0 type dummy ip link add link dummy0 name vlan1 type vlan id 1 ip link set vlan1 up for i in {2..55} do let A=$i-1 ip link add vlan$i link vlan$A type vlan id $i done ip link del dummy0 Splat looks like: [ 155.513226][ T908] BUG: KASAN: use-after-free in __unwind_start+0x71/0x850 [ 155.514162][ T908] Write of size 88 at addr ffff8880608a6cc0 by task ip/908 [ 155.515048][ T908] [ 155.515333][ T908] CPU: 0 PID: 908 Comm: ip Not tainted 5.4.0-rc3+ #96 [ 155.516147][ T908] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 155.517233][ T908] Call Trace: [ 155.517627][ T908] [ 155.517918][ T908] Allocated by task 0: [ 155.518412][ T908] (stack is not available) [ 155.518955][ T908] [ 155.519228][ T908] Freed by task 0: [ 155.519885][ T908] (stack is not available) [ 155.520452][ T908] [ 155.520729][ T908] The buggy address belongs to the object at ffff8880608a6ac0 [ 155.520729][ T908] which belongs to the cache names_cache of size 4096 [ 155.522387][ T908] The buggy address is located 512 bytes inside of [ 155.522387][ T908] 4096-byte region [ffff8880608a6ac0, ffff8880608a7ac0) [ 155.523920][ T908] The buggy address belongs to the page: [ 155.524552][ T908] page:ffffea0001822800 refcount:1 mapcount:0 mapping:ffff88806c657cc0 index:0x0 compound_mapcount:0 [ 155.525836][ T908] flags: 0x100000000010200(slab|head) [ 155.526445][ T908] raw: 0100000000010200 ffffea0001813808 ffffea0001a26c08 ffff88806c657cc0 [ 155.527424][ T908] raw: 0000000000000000 0000000000070007 00000001ffffffff 0000000000000000 [ 155.528429][ T908] page dumped because: kasan: bad access detected [ 155.529158][ T908] [ 155.529410][ T908] Memory state around the buggy address: [ 155.530060][ T908] ffff8880608a6b80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 155.530971][ T908] ffff8880608a6c00: fb fb fb fb fb f1 f1 f1 f1 00 f2 f2 f2 f3 f3 f3 [ 155.531889][ T908] >ffff8880608a6c80: f3 fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 155.532806][ T908] ^ [ 155.533509][ T908] ffff8880608a6d00: fb fb fb fb fb fb fb fb fb f1 f1 f1 f1 00 00 00 [ 155.534436][ T908] ffff8880608a6d80: f2 f3 f3 f3 f3 fb fb fb 00 00 00 00 00 00 00 00 [ ... ] Signed-off-by: Taehee Yoo <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 67f028a commit a2e0655

File tree

2 files changed

+231
-45
lines changed

2 files changed

+231
-45
lines changed

include/linux/netdevice.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,8 @@ enum netdev_priv_flags {
16191619
* @perm_addr: Permanent hw address
16201620
* @addr_assign_type: Hw address assignment type
16211621
* @addr_len: Hardware address length
1622+
* @upper_level: Maximum depth level of upper devices.
1623+
* @lower_level: Maximum depth level of lower devices.
16221624
* @neigh_priv_len: Used in neigh_alloc()
16231625
* @dev_id: Used to differentiate devices that share
16241626
* the same link layer address
@@ -1853,6 +1855,8 @@ struct net_device {
18531855
unsigned char perm_addr[MAX_ADDR_LEN];
18541856
unsigned char addr_assign_type;
18551857
unsigned char addr_len;
1858+
unsigned char upper_level;
1859+
unsigned char lower_level;
18561860
unsigned short neigh_priv_len;
18571861
unsigned short dev_id;
18581862
unsigned short dev_port;

net/core/dev.c

Lines changed: 227 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
#include "net-sysfs.h"
150150

151151
#define MAX_GRO_SKBS 8
152+
#define MAX_NEST_DEV 8
152153

153154
/* This should be increased if a protocol with a bigger head is added. */
154155
#define GRO_MAX_HEAD (MAX_HEADER + 128)
@@ -6542,6 +6543,21 @@ struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
65426543
}
65436544
EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu);
65446545

6546+
static struct net_device *netdev_next_upper_dev(struct net_device *dev,
6547+
struct list_head **iter)
6548+
{
6549+
struct netdev_adjacent *upper;
6550+
6551+
upper = list_entry((*iter)->next, struct netdev_adjacent, list);
6552+
6553+
if (&upper->list == &dev->adj_list.upper)
6554+
return NULL;
6555+
6556+
*iter = &upper->list;
6557+
6558+
return upper->dev;
6559+
}
6560+
65456561
static struct net_device *netdev_next_upper_dev_rcu(struct net_device *dev,
65466562
struct list_head **iter)
65476563
{
@@ -6559,28 +6575,93 @@ static struct net_device *netdev_next_upper_dev_rcu(struct net_device *dev,
65596575
return upper->dev;
65606576
}
65616577

6578+
static int netdev_walk_all_upper_dev(struct net_device *dev,
6579+
int (*fn)(struct net_device *dev,
6580+
void *data),
6581+
void *data)
6582+
{
6583+
struct net_device *udev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
6584+
struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
6585+
int ret, cur = 0;
6586+
6587+
now = dev;
6588+
iter = &dev->adj_list.upper;
6589+
6590+
while (1) {
6591+
if (now != dev) {
6592+
ret = fn(now, data);
6593+
if (ret)
6594+
return ret;
6595+
}
6596+
6597+
next = NULL;
6598+
while (1) {
6599+
udev = netdev_next_upper_dev(now, &iter);
6600+
if (!udev)
6601+
break;
6602+
6603+
next = udev;
6604+
niter = &udev->adj_list.upper;
6605+
dev_stack[cur] = now;
6606+
iter_stack[cur++] = iter;
6607+
break;
6608+
}
6609+
6610+
if (!next) {
6611+
if (!cur)
6612+
return 0;
6613+
next = dev_stack[--cur];
6614+
niter = iter_stack[cur];
6615+
}
6616+
6617+
now = next;
6618+
iter = niter;
6619+
}
6620+
6621+
return 0;
6622+
}
6623+
65626624
int netdev_walk_all_upper_dev_rcu(struct net_device *dev,
65636625
int (*fn)(struct net_device *dev,
65646626
void *data),
65656627
void *data)
65666628
{
6567-
struct net_device *udev;
6568-
struct list_head *iter;
6569-
int ret;
6629+
struct net_device *udev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
6630+
struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
6631+
int ret, cur = 0;
65706632

6571-
for (iter = &dev->adj_list.upper,
6572-
udev = netdev_next_upper_dev_rcu(dev, &iter);
6573-
udev;
6574-
udev = netdev_next_upper_dev_rcu(dev, &iter)) {
6575-
/* first is the upper device itself */
6576-
ret = fn(udev, data);
6577-
if (ret)
6578-
return ret;
6633+
now = dev;
6634+
iter = &dev->adj_list.upper;
65796635

6580-
/* then look at all of its upper devices */
6581-
ret = netdev_walk_all_upper_dev_rcu(udev, fn, data);
6582-
if (ret)
6583-
return ret;
6636+
while (1) {
6637+
if (now != dev) {
6638+
ret = fn(now, data);
6639+
if (ret)
6640+
return ret;
6641+
}
6642+
6643+
next = NULL;
6644+
while (1) {
6645+
udev = netdev_next_upper_dev_rcu(now, &iter);
6646+
if (!udev)
6647+
break;
6648+
6649+
next = udev;
6650+
niter = &udev->adj_list.upper;
6651+
dev_stack[cur] = now;
6652+
iter_stack[cur++] = iter;
6653+
break;
6654+
}
6655+
6656+
if (!next) {
6657+
if (!cur)
6658+
return 0;
6659+
next = dev_stack[--cur];
6660+
niter = iter_stack[cur];
6661+
}
6662+
6663+
now = next;
6664+
iter = niter;
65846665
}
65856666

65866667
return 0;
@@ -6688,23 +6769,42 @@ int netdev_walk_all_lower_dev(struct net_device *dev,
66886769
void *data),
66896770
void *data)
66906771
{
6691-
struct net_device *ldev;
6692-
struct list_head *iter;
6693-
int ret;
6772+
struct net_device *ldev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
6773+
struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
6774+
int ret, cur = 0;
66946775

6695-
for (iter = &dev->adj_list.lower,
6696-
ldev = netdev_next_lower_dev(dev, &iter);
6697-
ldev;
6698-
ldev = netdev_next_lower_dev(dev, &iter)) {
6699-
/* first is the lower device itself */
6700-
ret = fn(ldev, data);
6701-
if (ret)
6702-
return ret;
6776+
now = dev;
6777+
iter = &dev->adj_list.lower;
67036778

6704-
/* then look at all of its lower devices */
6705-
ret = netdev_walk_all_lower_dev(ldev, fn, data);
6706-
if (ret)
6707-
return ret;
6779+
while (1) {
6780+
if (now != dev) {
6781+
ret = fn(now, data);
6782+
if (ret)
6783+
return ret;
6784+
}
6785+
6786+
next = NULL;
6787+
while (1) {
6788+
ldev = netdev_next_lower_dev(now, &iter);
6789+
if (!ldev)
6790+
break;
6791+
6792+
next = ldev;
6793+
niter = &ldev->adj_list.lower;
6794+
dev_stack[cur] = now;
6795+
iter_stack[cur++] = iter;
6796+
break;
6797+
}
6798+
6799+
if (!next) {
6800+
if (!cur)
6801+
return 0;
6802+
next = dev_stack[--cur];
6803+
niter = iter_stack[cur];
6804+
}
6805+
6806+
now = next;
6807+
iter = niter;
67086808
}
67096809

67106810
return 0;
@@ -6725,28 +6825,93 @@ static struct net_device *netdev_next_lower_dev_rcu(struct net_device *dev,
67256825
return lower->dev;
67266826
}
67276827

6728-
int netdev_walk_all_lower_dev_rcu(struct net_device *dev,
6729-
int (*fn)(struct net_device *dev,
6730-
void *data),
6731-
void *data)
6828+
static u8 __netdev_upper_depth(struct net_device *dev)
6829+
{
6830+
struct net_device *udev;
6831+
struct list_head *iter;
6832+
u8 max_depth = 0;
6833+
6834+
for (iter = &dev->adj_list.upper,
6835+
udev = netdev_next_upper_dev(dev, &iter);
6836+
udev;
6837+
udev = netdev_next_upper_dev(dev, &iter)) {
6838+
if (max_depth < udev->upper_level)
6839+
max_depth = udev->upper_level;
6840+
}
6841+
6842+
return max_depth;
6843+
}
6844+
6845+
static u8 __netdev_lower_depth(struct net_device *dev)
67326846
{
67336847
struct net_device *ldev;
67346848
struct list_head *iter;
6735-
int ret;
6849+
u8 max_depth = 0;
67366850

67376851
for (iter = &dev->adj_list.lower,
6738-
ldev = netdev_next_lower_dev_rcu(dev, &iter);
6852+
ldev = netdev_next_lower_dev(dev, &iter);
67396853
ldev;
6740-
ldev = netdev_next_lower_dev_rcu(dev, &iter)) {
6741-
/* first is the lower device itself */
6742-
ret = fn(ldev, data);
6743-
if (ret)
6744-
return ret;
6854+
ldev = netdev_next_lower_dev(dev, &iter)) {
6855+
if (max_depth < ldev->lower_level)
6856+
max_depth = ldev->lower_level;
6857+
}
67456858

6746-
/* then look at all of its lower devices */
6747-
ret = netdev_walk_all_lower_dev_rcu(ldev, fn, data);
6748-
if (ret)
6749-
return ret;
6859+
return max_depth;
6860+
}
6861+
6862+
static int __netdev_update_upper_level(struct net_device *dev, void *data)
6863+
{
6864+
dev->upper_level = __netdev_upper_depth(dev) + 1;
6865+
return 0;
6866+
}
6867+
6868+
static int __netdev_update_lower_level(struct net_device *dev, void *data)
6869+
{
6870+
dev->lower_level = __netdev_lower_depth(dev) + 1;
6871+
return 0;
6872+
}
6873+
6874+
int netdev_walk_all_lower_dev_rcu(struct net_device *dev,
6875+
int (*fn)(struct net_device *dev,
6876+
void *data),
6877+
void *data)
6878+
{
6879+
struct net_device *ldev, *next, *now, *dev_stack[MAX_NEST_DEV + 1];
6880+
struct list_head *niter, *iter, *iter_stack[MAX_NEST_DEV + 1];
6881+
int ret, cur = 0;
6882+
6883+
now = dev;
6884+
iter = &dev->adj_list.lower;
6885+
6886+
while (1) {
6887+
if (now != dev) {
6888+
ret = fn(now, data);
6889+
if (ret)
6890+
return ret;
6891+
}
6892+
6893+
next = NULL;
6894+
while (1) {
6895+
ldev = netdev_next_lower_dev_rcu(now, &iter);
6896+
if (!ldev)
6897+
break;
6898+
6899+
next = ldev;
6900+
niter = &ldev->adj_list.lower;
6901+
dev_stack[cur] = now;
6902+
iter_stack[cur++] = iter;
6903+
break;
6904+
}
6905+
6906+
if (!next) {
6907+
if (!cur)
6908+
return 0;
6909+
next = dev_stack[--cur];
6910+
niter = iter_stack[cur];
6911+
}
6912+
6913+
now = next;
6914+
iter = niter;
67506915
}
67516916

67526917
return 0;
@@ -7003,6 +7168,9 @@ static int __netdev_upper_dev_link(struct net_device *dev,
70037168
if (netdev_has_upper_dev(upper_dev, dev))
70047169
return -EBUSY;
70057170

7171+
if ((dev->lower_level + upper_dev->upper_level) > MAX_NEST_DEV)
7172+
return -EMLINK;
7173+
70067174
if (!master) {
70077175
if (netdev_has_upper_dev(dev, upper_dev))
70087176
return -EEXIST;
@@ -7029,6 +7197,12 @@ static int __netdev_upper_dev_link(struct net_device *dev,
70297197
if (ret)
70307198
goto rollback;
70317199

7200+
__netdev_update_upper_level(dev, NULL);
7201+
netdev_walk_all_lower_dev(dev, __netdev_update_upper_level, NULL);
7202+
7203+
__netdev_update_lower_level(upper_dev, NULL);
7204+
netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level, NULL);
7205+
70327206
return 0;
70337207

70347208
rollback:
@@ -7111,6 +7285,12 @@ void netdev_upper_dev_unlink(struct net_device *dev,
71117285

71127286
call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
71137287
&changeupper_info.info);
7288+
7289+
__netdev_update_upper_level(dev, NULL);
7290+
netdev_walk_all_lower_dev(dev, __netdev_update_upper_level, NULL);
7291+
7292+
__netdev_update_lower_level(upper_dev, NULL);
7293+
netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level, NULL);
71147294
}
71157295
EXPORT_SYMBOL(netdev_upper_dev_unlink);
71167296

@@ -8978,6 +9158,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
89789158

89799159
dev->gso_max_size = GSO_MAX_SIZE;
89809160
dev->gso_max_segs = GSO_MAX_SEGS;
9161+
dev->upper_level = 1;
9162+
dev->lower_level = 1;
89819163

89829164
INIT_LIST_HEAD(&dev->napi_list);
89839165
INIT_LIST_HEAD(&dev->unreg_list);

0 commit comments

Comments
 (0)