Skip to content

Commit 062b3e1

Browse files
sammjdavem330
authored andcommitted
net/ncsi: Refactor MAC, VLAN filters
The NCSI driver defines a generic ncsi_channel_filter struct that can be used to store arbitrarily formatted filters, and several generic methods of accessing data stored in such a filter. However in both the driver and as defined in the NCSI specification there are only two actual filters: VLAN ID filters and MAC address filters. The splitting of the MAC filter into unicast, multicast, and mixed is also technically not necessary as these are stored in the same location in hardware. To save complexity, particularly in the set up and accessing of these generic filters, remove them in favour of two specific structs. These can be acted on directly and do not need several generic helper functions to use. This also fixes a memory error found by KASAN on ARM32 (which is not upstream yet), where response handlers accessing a filter's data field could write past allocated memory. [ 114.926512] ================================================================== [ 114.933861] BUG: KASAN: slab-out-of-bounds in ncsi_configure_channel+0x4b8/0xc58 [ 114.941304] Read of size 2 at addr 94888558 by task kworker/0:2/546 [ 114.947593] [ 114.949146] CPU: 0 PID: 546 Comm: kworker/0:2 Not tainted 4.16.0-rc6-00119-ge156398bfcad #13 ... [ 115.170233] The buggy address belongs to the object at 94888540 [ 115.170233] which belongs to the cache kmalloc-32 of size 32 [ 115.181917] The buggy address is located 24 bytes inside of [ 115.181917] 32-byte region [94888540, 94888560) [ 115.192115] The buggy address belongs to the page: [ 115.196943] page:9eeac100 count:1 mapcount:0 mapping:94888000 index:0x94888fc1 [ 115.204200] flags: 0x100(slab) [ 115.207330] raw: 00000100 94888000 94888fc1 0000003f 00000001 9eea2014 9eecaa74 96c003e0 [ 115.215444] page dumped because: kasan: bad access detected [ 115.221036] [ 115.222544] Memory state around the buggy address: [ 115.227384] 94888400: fb fb fb fb fc fc fc fc 04 fc fc fc fc fc fc fc [ 115.233959] 94888480: 00 00 00 fc fc fc fc fc 00 04 fc fc fc fc fc fc [ 115.240529] >94888500: 00 00 04 fc fc fc fc fc 00 00 04 fc fc fc fc fc [ 115.247077] ^ [ 115.252523] 94888580: 00 04 fc fc fc fc fc fc 06 fc fc fc fc fc fc fc [ 115.259093] 94888600: 00 00 06 fc fc fc fc fc 00 00 04 fc fc fc fc fc [ 115.265639] ================================================================== Reported-by: Joel Stanley <[email protected]> Signed-off-by: Samuel Mendoza-Jonas <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c210f7b commit 062b3e1

File tree

4 files changed

+147
-311
lines changed

4 files changed

+147
-311
lines changed

net/ncsi/internal.h

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,6 @@ enum {
6868
NCSI_MODE_MAX
6969
};
7070

71-
enum {
72-
NCSI_FILTER_BASE = 0,
73-
NCSI_FILTER_VLAN = 0,
74-
NCSI_FILTER_UC,
75-
NCSI_FILTER_MC,
76-
NCSI_FILTER_MIXED,
77-
NCSI_FILTER_MAX
78-
};
79-
8071
struct ncsi_channel_version {
8172
u32 version; /* Supported BCD encoded NCSI version */
8273
u32 alpha2; /* Supported BCD encoded NCSI version */
@@ -98,11 +89,18 @@ struct ncsi_channel_mode {
9889
u32 data[8]; /* Data entries */
9990
};
10091

101-
struct ncsi_channel_filter {
102-
u32 index; /* Index of channel filters */
103-
u32 total; /* Total entries in the filter table */
104-
u64 bitmap; /* Bitmap of valid entries */
105-
u32 data[]; /* Data for the valid entries */
92+
struct ncsi_channel_mac_filter {
93+
u8 n_uc;
94+
u8 n_mc;
95+
u8 n_mixed;
96+
u64 bitmap;
97+
unsigned char *addrs;
98+
};
99+
100+
struct ncsi_channel_vlan_filter {
101+
u8 n_vids;
102+
u64 bitmap;
103+
u16 *vids;
106104
};
107105

108106
struct ncsi_channel_stats {
@@ -186,7 +184,9 @@ struct ncsi_channel {
186184
struct ncsi_channel_version version;
187185
struct ncsi_channel_cap caps[NCSI_CAP_MAX];
188186
struct ncsi_channel_mode modes[NCSI_MODE_MAX];
189-
struct ncsi_channel_filter *filters[NCSI_FILTER_MAX];
187+
/* Filtering Settings */
188+
struct ncsi_channel_mac_filter mac_filter;
189+
struct ncsi_channel_vlan_filter vlan_filter;
190190
struct ncsi_channel_stats stats;
191191
struct {
192192
struct timer_list timer;
@@ -320,10 +320,6 @@ extern spinlock_t ncsi_dev_lock;
320320
list_for_each_entry_rcu(nc, &np->channels, node)
321321

322322
/* Resources */
323-
u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index);
324-
int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data);
325-
int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data);
326-
int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
327323
void ncsi_start_channel_monitor(struct ncsi_channel *nc);
328324
void ncsi_stop_channel_monitor(struct ncsi_channel *nc);
329325
struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,

net/ncsi/ncsi-manage.c

Lines changed: 52 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -27,125 +27,6 @@
2727
LIST_HEAD(ncsi_dev_list);
2828
DEFINE_SPINLOCK(ncsi_dev_lock);
2929

30-
static inline int ncsi_filter_size(int table)
31-
{
32-
int sizes[] = { 2, 6, 6, 6 };
33-
34-
BUILD_BUG_ON(ARRAY_SIZE(sizes) != NCSI_FILTER_MAX);
35-
if (table < NCSI_FILTER_BASE || table >= NCSI_FILTER_MAX)
36-
return -EINVAL;
37-
38-
return sizes[table];
39-
}
40-
41-
u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
42-
{
43-
struct ncsi_channel_filter *ncf;
44-
int size;
45-
46-
ncf = nc->filters[table];
47-
if (!ncf)
48-
return NULL;
49-
50-
size = ncsi_filter_size(table);
51-
if (size < 0)
52-
return NULL;
53-
54-
return ncf->data + size * index;
55-
}
56-
57-
/* Find the first active filter in a filter table that matches the given
58-
* data parameter. If data is NULL, this returns the first active filter.
59-
*/
60-
int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
61-
{
62-
struct ncsi_channel_filter *ncf;
63-
void *bitmap;
64-
int index, size;
65-
unsigned long flags;
66-
67-
ncf = nc->filters[table];
68-
if (!ncf)
69-
return -ENXIO;
70-
71-
size = ncsi_filter_size(table);
72-
if (size < 0)
73-
return size;
74-
75-
spin_lock_irqsave(&nc->lock, flags);
76-
bitmap = (void *)&ncf->bitmap;
77-
index = -1;
78-
while ((index = find_next_bit(bitmap, ncf->total, index + 1))
79-
< ncf->total) {
80-
if (!data || !memcmp(ncf->data + size * index, data, size)) {
81-
spin_unlock_irqrestore(&nc->lock, flags);
82-
return index;
83-
}
84-
}
85-
spin_unlock_irqrestore(&nc->lock, flags);
86-
87-
return -ENOENT;
88-
}
89-
90-
int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data)
91-
{
92-
struct ncsi_channel_filter *ncf;
93-
int index, size;
94-
void *bitmap;
95-
unsigned long flags;
96-
97-
size = ncsi_filter_size(table);
98-
if (size < 0)
99-
return size;
100-
101-
index = ncsi_find_filter(nc, table, data);
102-
if (index >= 0)
103-
return index;
104-
105-
ncf = nc->filters[table];
106-
if (!ncf)
107-
return -ENODEV;
108-
109-
spin_lock_irqsave(&nc->lock, flags);
110-
bitmap = (void *)&ncf->bitmap;
111-
do {
112-
index = find_next_zero_bit(bitmap, ncf->total, 0);
113-
if (index >= ncf->total) {
114-
spin_unlock_irqrestore(&nc->lock, flags);
115-
return -ENOSPC;
116-
}
117-
} while (test_and_set_bit(index, bitmap));
118-
119-
memcpy(ncf->data + size * index, data, size);
120-
spin_unlock_irqrestore(&nc->lock, flags);
121-
122-
return index;
123-
}
124-
125-
int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index)
126-
{
127-
struct ncsi_channel_filter *ncf;
128-
int size;
129-
void *bitmap;
130-
unsigned long flags;
131-
132-
size = ncsi_filter_size(table);
133-
if (size < 0)
134-
return size;
135-
136-
ncf = nc->filters[table];
137-
if (!ncf || index >= ncf->total)
138-
return -ENODEV;
139-
140-
spin_lock_irqsave(&nc->lock, flags);
141-
bitmap = (void *)&ncf->bitmap;
142-
if (test_and_clear_bit(index, bitmap))
143-
memset(ncf->data + size * index, 0, size);
144-
spin_unlock_irqrestore(&nc->lock, flags);
145-
146-
return 0;
147-
}
148-
14930
static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
15031
{
15132
struct ncsi_dev *nd = &ndp->ndev;
@@ -339,20 +220,13 @@ struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
339220
static void ncsi_remove_channel(struct ncsi_channel *nc)
340221
{
341222
struct ncsi_package *np = nc->package;
342-
struct ncsi_channel_filter *ncf;
343223
unsigned long flags;
344-
int i;
345224

346-
/* Release filters */
347225
spin_lock_irqsave(&nc->lock, flags);
348-
for (i = 0; i < NCSI_FILTER_MAX; i++) {
349-
ncf = nc->filters[i];
350-
if (!ncf)
351-
continue;
352226

353-
nc->filters[i] = NULL;
354-
kfree(ncf);
355-
}
227+
/* Release filters */
228+
kfree(nc->mac_filter.addrs);
229+
kfree(nc->vlan_filter.vids);
356230

357231
nc->state = NCSI_CHANNEL_INACTIVE;
358232
spin_unlock_irqrestore(&nc->lock, flags);
@@ -670,32 +544,26 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
670544
static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
671545
struct ncsi_cmd_arg *nca)
672546
{
547+
struct ncsi_channel_vlan_filter *ncf;
548+
unsigned long flags;
549+
void *bitmap;
673550
int index;
674-
u32 *data;
675551
u16 vid;
676552

677-
index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, NULL);
678-
if (index < 0) {
679-
/* Filter table empty */
680-
return -1;
681-
}
553+
ncf = &nc->vlan_filter;
554+
bitmap = &ncf->bitmap;
682555

683-
data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index);
684-
if (!data) {
685-
netdev_err(ndp->ndev.dev,
686-
"NCSI: failed to retrieve filter %d\n", index);
687-
/* Set the VLAN id to 0 - this will still disable the entry in
688-
* the filter table, but we won't know what it was.
689-
*/
690-
vid = 0;
691-
} else {
692-
vid = *(u16 *)data;
556+
spin_lock_irqsave(&nc->lock, flags);
557+
index = find_next_bit(bitmap, ncf->n_vids, 0);
558+
if (index >= ncf->n_vids) {
559+
spin_unlock_irqrestore(&nc->lock, flags);
560+
return -1;
693561
}
562+
vid = ncf->vids[index];
694563

695-
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
696-
"NCSI: removed vlan tag %u at index %d\n",
697-
vid, index + 1);
698-
ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index);
564+
clear_bit(index, bitmap);
565+
ncf->vids[index] = 0;
566+
spin_unlock_irqrestore(&nc->lock, flags);
699567

700568
nca->type = NCSI_PKT_CMD_SVF;
701569
nca->words[1] = vid;
@@ -711,45 +579,55 @@ static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
711579
static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
712580
struct ncsi_cmd_arg *nca)
713581
{
582+
struct ncsi_channel_vlan_filter *ncf;
714583
struct vlan_vid *vlan = NULL;
715-
int index = 0;
584+
unsigned long flags;
585+
int i, index;
586+
void *bitmap;
587+
u16 vid;
716588

589+
if (list_empty(&ndp->vlan_vids))
590+
return -1;
591+
592+
ncf = &nc->vlan_filter;
593+
bitmap = &ncf->bitmap;
594+
595+
spin_lock_irqsave(&nc->lock, flags);
596+
597+
rcu_read_lock();
717598
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
718-
index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
719-
if (index < 0) {
720-
/* New tag to add */
721-
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
722-
"NCSI: new vlan id to set: %u\n",
723-
vlan->vid);
599+
vid = vlan->vid;
600+
for (i = 0; i < ncf->n_vids; i++)
601+
if (ncf->vids[i] == vid) {
602+
vid = 0;
603+
break;
604+
}
605+
if (vid)
724606
break;
725-
}
726-
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
727-
"vid %u already at filter pos %d\n",
728-
vlan->vid, index);
729607
}
608+
rcu_read_unlock();
730609

731-
if (!vlan || index >= 0) {
732-
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
733-
"no vlan ids left to set\n");
610+
if (!vid) {
611+
/* No VLAN ID is not set */
612+
spin_unlock_irqrestore(&nc->lock, flags);
734613
return -1;
735614
}
736615

737-
index = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
738-
if (index < 0) {
616+
index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
617+
if (index < 0 || index >= ncf->n_vids) {
739618
netdev_err(ndp->ndev.dev,
740-
"Failed to add new VLAN tag, error %d\n", index);
741-
if (index == -ENOSPC)
742-
netdev_err(ndp->ndev.dev,
743-
"Channel %u already has all VLAN filters set\n",
744-
nc->id);
619+
"Channel %u already has all VLAN filters set\n",
620+
nc->id);
621+
spin_unlock_irqrestore(&nc->lock, flags);
745622
return -1;
746623
}
747624

748-
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
749-
"NCSI: set vid %u in packet, index %u\n",
750-
vlan->vid, index + 1);
625+
ncf->vids[index] = vid;
626+
set_bit(index, bitmap);
627+
spin_unlock_irqrestore(&nc->lock, flags);
628+
751629
nca->type = NCSI_PKT_CMD_SVF;
752-
nca->words[1] = vlan->vid;
630+
nca->words[1] = vid;
753631
/* HW filter index starts at 1 */
754632
nca->bytes[6] = index + 1;
755633
nca->bytes[7] = 0x01;

net/ncsi/ncsi-netlink.c

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,9 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
5858
struct ncsi_dev_priv *ndp,
5959
struct ncsi_channel *nc)
6060
{
61-
struct nlattr *vid_nest;
62-
struct ncsi_channel_filter *ncf;
61+
struct ncsi_channel_vlan_filter *ncf;
6362
struct ncsi_channel_mode *m;
64-
u32 *data;
63+
struct nlattr *vid_nest;
6564
int i;
6665

6766
nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
@@ -79,18 +78,13 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
7978
vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
8079
if (!vid_nest)
8180
return -ENOMEM;
82-
ncf = nc->filters[NCSI_FILTER_VLAN];
81+
ncf = &nc->vlan_filter;
8382
i = -1;
84-
if (ncf) {
85-
while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total,
86-
i + 1)) < ncf->total) {
87-
data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, i);
88-
/* Uninitialised channels will have 'zero' vlan ids */
89-
if (!data || !*data)
90-
continue;
83+
while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
84+
i + 1)) < ncf->n_vids) {
85+
if (ncf->vids[i])
9186
nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
92-
*(u16 *)data);
93-
}
87+
ncf->vids[i]);
9488
}
9589
nla_nest_end(skb, vid_nest);
9690

0 commit comments

Comments
 (0)