diff options
author | Dan Williams <dan.j.williams@intel.com> | 2016-01-05 21:37:23 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2016-01-05 21:37:23 -0500 |
commit | e07ecd76d4db7bda1e9495395b2110a3fe28845a (patch) | |
tree | 731c4008d67d1e0ae1267f3f28d38a2abf9281c5 | |
parent | 0731de0dd95b251ed6cfb5f132486e52357fce53 (diff) |
libnvdimm: fix namespace object confusion in is_uuid_busy()
When btt devices were re-worked to be child devices of regions this
routine was overlooked. It mistakenly attempts to_nd_namespace_pmem()
or to_nd_namespace_blk() conversions on btt and pfn devices. By luck to
date we have happened to be hitting valid memory leading to a uuid
miscompare, but a recent change to struct nd_namespace_common causes:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000001
IP: [<ffffffff814610dc>] memcmp+0xc/0x40
[..]
Call Trace:
[<ffffffffa0028631>] is_uuid_busy+0xc1/0x2a0 [libnvdimm]
[<ffffffffa0028570>] ? to_nd_blk_region+0x50/0x50 [libnvdimm]
[<ffffffff8158c9c0>] device_for_each_child+0x50/0x90
Cc: <stable@vger.kernel.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r-- | drivers/nvdimm/namespace_devs.c | 53 | ||||
-rw-r--r-- | drivers/nvdimm/region_devs.c | 56 |
2 files changed, 53 insertions, 56 deletions
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index ee6dee41155a..8ebfcaae3f5a 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c | |||
@@ -77,6 +77,59 @@ static bool is_namespace_io(struct device *dev) | |||
77 | return dev ? dev->type == &namespace_io_device_type : false; | 77 | return dev ? dev->type == &namespace_io_device_type : false; |
78 | } | 78 | } |
79 | 79 | ||
80 | static int is_uuid_busy(struct device *dev, void *data) | ||
81 | { | ||
82 | u8 *uuid1 = data, *uuid2 = NULL; | ||
83 | |||
84 | if (is_namespace_pmem(dev)) { | ||
85 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); | ||
86 | |||
87 | uuid2 = nspm->uuid; | ||
88 | } else if (is_namespace_blk(dev)) { | ||
89 | struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); | ||
90 | |||
91 | uuid2 = nsblk->uuid; | ||
92 | } else if (is_nd_btt(dev)) { | ||
93 | struct nd_btt *nd_btt = to_nd_btt(dev); | ||
94 | |||
95 | uuid2 = nd_btt->uuid; | ||
96 | } else if (is_nd_pfn(dev)) { | ||
97 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | ||
98 | |||
99 | uuid2 = nd_pfn->uuid; | ||
100 | } | ||
101 | |||
102 | if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0) | ||
103 | return -EBUSY; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int is_namespace_uuid_busy(struct device *dev, void *data) | ||
109 | { | ||
110 | if (is_nd_pmem(dev) || is_nd_blk(dev)) | ||
111 | return device_for_each_child(dev, data, is_uuid_busy); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * nd_is_uuid_unique - verify that no other namespace has @uuid | ||
117 | * @dev: any device on a nvdimm_bus | ||
118 | * @uuid: uuid to check | ||
119 | */ | ||
120 | bool nd_is_uuid_unique(struct device *dev, u8 *uuid) | ||
121 | { | ||
122 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); | ||
123 | |||
124 | if (!nvdimm_bus) | ||
125 | return false; | ||
126 | WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev)); | ||
127 | if (device_for_each_child(&nvdimm_bus->dev, uuid, | ||
128 | is_namespace_uuid_busy) != 0) | ||
129 | return false; | ||
130 | return true; | ||
131 | } | ||
132 | |||
80 | bool pmem_should_map_pages(struct device *dev) | 133 | bool pmem_should_map_pages(struct device *dev) |
81 | { | 134 | { |
82 | struct nd_region *nd_region = to_nd_region(dev->parent); | 135 | struct nd_region *nd_region = to_nd_region(dev->parent); |
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 9c632f73915e..139bf71ca549 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c | |||
@@ -134,62 +134,6 @@ int nd_region_to_nstype(struct nd_region *nd_region) | |||
134 | } | 134 | } |
135 | EXPORT_SYMBOL(nd_region_to_nstype); | 135 | EXPORT_SYMBOL(nd_region_to_nstype); |
136 | 136 | ||
137 | static int is_uuid_busy(struct device *dev, void *data) | ||
138 | { | ||
139 | struct nd_region *nd_region = to_nd_region(dev->parent); | ||
140 | u8 *uuid = data; | ||
141 | |||
142 | switch (nd_region_to_nstype(nd_region)) { | ||
143 | case ND_DEVICE_NAMESPACE_PMEM: { | ||
144 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); | ||
145 | |||
146 | if (!nspm->uuid) | ||
147 | break; | ||
148 | if (memcmp(uuid, nspm->uuid, NSLABEL_UUID_LEN) == 0) | ||
149 | return -EBUSY; | ||
150 | break; | ||
151 | } | ||
152 | case ND_DEVICE_NAMESPACE_BLK: { | ||
153 | struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); | ||
154 | |||
155 | if (!nsblk->uuid) | ||
156 | break; | ||
157 | if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) == 0) | ||
158 | return -EBUSY; | ||
159 | break; | ||
160 | } | ||
161 | default: | ||
162 | break; | ||
163 | } | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int is_namespace_uuid_busy(struct device *dev, void *data) | ||
169 | { | ||
170 | if (is_nd_pmem(dev) || is_nd_blk(dev)) | ||
171 | return device_for_each_child(dev, data, is_uuid_busy); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * nd_is_uuid_unique - verify that no other namespace has @uuid | ||
177 | * @dev: any device on a nvdimm_bus | ||
178 | * @uuid: uuid to check | ||
179 | */ | ||
180 | bool nd_is_uuid_unique(struct device *dev, u8 *uuid) | ||
181 | { | ||
182 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); | ||
183 | |||
184 | if (!nvdimm_bus) | ||
185 | return false; | ||
186 | WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev)); | ||
187 | if (device_for_each_child(&nvdimm_bus->dev, uuid, | ||
188 | is_namespace_uuid_busy) != 0) | ||
189 | return false; | ||
190 | return true; | ||
191 | } | ||
192 | |||
193 | static ssize_t size_show(struct device *dev, | 137 | static ssize_t size_show(struct device *dev, |
194 | struct device_attribute *attr, char *buf) | 138 | struct device_attribute *attr, char *buf) |
195 | { | 139 | { |