]>
Commit | Line | Data |
---|---|---|
937cd4b4 GKH |
1 | From 546481c2816ea3c061ee9d5658eb48070f69212e Mon Sep 17 00:00:00 2001 |
2 | From: Erez Shitrit <erezsh@mellanox.com> | |
3 | Date: Sun, 28 Aug 2016 10:58:31 +0300 | |
4 | Subject: IB/ipoib: Fix memory corruption in ipoib cm mode connect flow | |
5 | ||
6 | From: Erez Shitrit <erezsh@mellanox.com> | |
7 | ||
8 | commit 546481c2816ea3c061ee9d5658eb48070f69212e upstream. | |
9 | ||
10 | When a new CM connection is being requested, ipoib driver copies data | |
11 | from the path pointer in the CM/tx object, the path object might be | |
12 | invalid at the point and memory corruption will happened later when now | |
13 | the CM driver will try using that data. | |
14 | ||
15 | The next scenario demonstrates it: | |
16 | neigh_add_path --> ipoib_cm_create_tx --> | |
17 | queue_work (pointer to path is in the cm/tx struct) | |
18 | #while the work is still in the queue, | |
19 | #the port goes down and causes the ipoib_flush_paths: | |
20 | ipoib_flush_paths --> path_free --> kfree(path) | |
21 | #at this point the work scheduled starts. | |
22 | ipoib_cm_tx_start --> copy from the (invalid)path pointer: | |
23 | (memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);) | |
24 | -> memory corruption. | |
25 | ||
26 | To fix that the driver now starts the CM/tx connection only if that | |
27 | specific path exists in the general paths database. | |
28 | This check is protected with the relevant locks, and uses the gid from | |
29 | the neigh member in the CM/tx object which is valid according to the ref | |
30 | count that was taken by the CM/tx. | |
31 | ||
32 | Fixes: 839fcaba35 ('IPoIB: Connected mode experimental support') | |
33 | Signed-off-by: Erez Shitrit <erezsh@mellanox.com> | |
34 | Signed-off-by: Leon Romanovsky <leon@kernel.org> | |
35 | Signed-off-by: Doug Ledford <dledford@redhat.com> | |
36 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
37 | ||
38 | --- | |
39 | drivers/infiniband/ulp/ipoib/ipoib.h | 1 + | |
40 | drivers/infiniband/ulp/ipoib/ipoib_cm.c | 16 ++++++++++++++++ | |
41 | drivers/infiniband/ulp/ipoib/ipoib_main.c | 2 +- | |
42 | 3 files changed, 18 insertions(+), 1 deletion(-) | |
43 | ||
44 | --- a/drivers/infiniband/ulp/ipoib/ipoib.h | |
45 | +++ b/drivers/infiniband/ulp/ipoib/ipoib.h | |
46 | @@ -478,6 +478,7 @@ void ipoib_send(struct net_device *dev, | |
47 | struct ipoib_ah *address, u32 qpn); | |
48 | void ipoib_reap_ah(struct work_struct *work); | |
49 | ||
50 | +struct ipoib_path *__path_find(struct net_device *dev, void *gid); | |
51 | void ipoib_mark_paths_invalid(struct net_device *dev); | |
52 | void ipoib_flush_paths(struct net_device *dev); | |
53 | int ipoib_check_sm_sendonly_fullmember_support(struct ipoib_dev_priv *priv); | |
54 | --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c | |
55 | +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c | |
56 | @@ -1318,6 +1318,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm | |
57 | } | |
58 | } | |
59 | ||
60 | +#define QPN_AND_OPTIONS_OFFSET 4 | |
61 | + | |
62 | static void ipoib_cm_tx_start(struct work_struct *work) | |
63 | { | |
64 | struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, | |
65 | @@ -1326,6 +1328,7 @@ static void ipoib_cm_tx_start(struct wor | |
66 | struct ipoib_neigh *neigh; | |
67 | struct ipoib_cm_tx *p; | |
68 | unsigned long flags; | |
69 | + struct ipoib_path *path; | |
70 | int ret; | |
71 | ||
72 | struct ib_sa_path_rec pathrec; | |
73 | @@ -1338,7 +1341,19 @@ static void ipoib_cm_tx_start(struct wor | |
74 | p = list_entry(priv->cm.start_list.next, typeof(*p), list); | |
75 | list_del_init(&p->list); | |
76 | neigh = p->neigh; | |
77 | + | |
78 | qpn = IPOIB_QPN(neigh->daddr); | |
79 | + /* | |
80 | + * As long as the search is with these 2 locks, | |
81 | + * path existence indicates its validity. | |
82 | + */ | |
83 | + path = __path_find(dev, neigh->daddr + QPN_AND_OPTIONS_OFFSET); | |
84 | + if (!path) { | |
85 | + pr_info("%s ignore not valid path %pI6\n", | |
86 | + __func__, | |
87 | + neigh->daddr + QPN_AND_OPTIONS_OFFSET); | |
88 | + goto free_neigh; | |
89 | + } | |
90 | memcpy(&pathrec, &p->path->pathrec, sizeof pathrec); | |
91 | ||
92 | spin_unlock_irqrestore(&priv->lock, flags); | |
93 | @@ -1350,6 +1365,7 @@ static void ipoib_cm_tx_start(struct wor | |
94 | spin_lock_irqsave(&priv->lock, flags); | |
95 | ||
96 | if (ret) { | |
97 | +free_neigh: | |
98 | neigh = p->neigh; | |
99 | if (neigh) { | |
100 | neigh->cm = NULL; | |
101 | --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c | |
102 | +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c | |
103 | @@ -485,7 +485,7 @@ int ipoib_set_mode(struct net_device *de | |
104 | return -EINVAL; | |
105 | } | |
106 | ||
107 | -static struct ipoib_path *__path_find(struct net_device *dev, void *gid) | |
108 | +struct ipoib_path *__path_find(struct net_device *dev, void *gid) | |
109 | { | |
110 | struct ipoib_dev_priv *priv = netdev_priv(dev); | |
111 | struct rb_node *n = priv->path_tree.rb_node; |