Skip to content

Commit 4fa59ed

Browse files
committed
virtio: acknowledge all features before access
The feature negotiation was designed in a way that makes it possible for devices to know which config fields will be accessed by drivers. This is broken since commit 404123c ("virtio: allow drivers to validate features") with fallout in at least block and net. We have a partial work-around in commit 2f9a174 ("virtio: write back F_VERSION_1 before validate") which at least lets devices find out which format should config space have, but this is a partial fix: guests should not access config space without acknowledging features since otherwise we'll never be able to change the config space format. To fix, split finalize_features from virtio_finalize_features and call finalize_features with all feature bits before validation, and then - if validation changed any bits - once again after. Since virtio_finalize_features no longer writes out features rename it to virtio_features_ok - since that is what it does: checks that features are ok with the device. As a side effect, this also reduces the amount of hypervisor accesses - we now only acknowledge features once unless we are clearing any features when validating (which is uncommon). IRC I think that this was more or less always the intent in the spec but unfortunately the way the spec is worded does not say this explicitly, I plan to address this at the spec level, too. Acked-by: Jason Wang <jasowang@redhat.com> Cc: stable@vger.kernel.org Fixes: 404123c ("virtio: allow drivers to validate features") Fixes: 2f9a174 ("virtio: write back F_VERSION_1 before validate") Cc: "Halil Pasic" <pasic@linux.ibm.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
1 parent 838d6d3 commit 4fa59ed

2 files changed

Lines changed: 24 additions & 18 deletions

File tree

drivers/virtio/virtio.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,13 @@ void virtio_add_status(struct virtio_device *dev, unsigned int status)
166166
}
167167
EXPORT_SYMBOL_GPL(virtio_add_status);
168168

169-
static int virtio_finalize_features(struct virtio_device *dev)
169+
/* Do some validation, then set FEATURES_OK */
170+
static int virtio_features_ok(struct virtio_device *dev)
170171
{
171-
int ret = dev->config->finalize_features(dev);
172172
unsigned status;
173+
int ret;
173174

174175
might_sleep();
175-
if (ret)
176-
return ret;
177176

178177
ret = arch_has_restricted_virtio_memory_access();
179178
if (ret) {
@@ -244,17 +243,6 @@ static int virtio_dev_probe(struct device *_d)
244243
driver_features_legacy = driver_features;
245244
}
246245

247-
/*
248-
* Some devices detect legacy solely via F_VERSION_1. Write
249-
* F_VERSION_1 to force LE config space accesses before FEATURES_OK for
250-
* these when needed.
251-
*/
252-
if (drv->validate && !virtio_legacy_is_little_endian()
253-
&& device_features & BIT_ULL(VIRTIO_F_VERSION_1)) {
254-
dev->features = BIT_ULL(VIRTIO_F_VERSION_1);
255-
dev->config->finalize_features(dev);
256-
}
257-
258246
if (device_features & (1ULL << VIRTIO_F_VERSION_1))
259247
dev->features = driver_features & device_features;
260248
else
@@ -265,13 +253,26 @@ static int virtio_dev_probe(struct device *_d)
265253
if (device_features & (1ULL << i))
266254
__virtio_set_bit(dev, i);
267255

256+
err = dev->config->finalize_features(dev);
257+
if (err)
258+
goto err;
259+
268260
if (drv->validate) {
261+
u64 features = dev->features;
262+
269263
err = drv->validate(dev);
270264
if (err)
271265
goto err;
266+
267+
/* Did validation change any features? Then write them again. */
268+
if (features != dev->features) {
269+
err = dev->config->finalize_features(dev);
270+
if (err)
271+
goto err;
272+
}
272273
}
273274

274-
err = virtio_finalize_features(dev);
275+
err = virtio_features_ok(dev);
275276
if (err)
276277
goto err;
277278

@@ -495,7 +496,11 @@ int virtio_device_restore(struct virtio_device *dev)
495496
/* We have a driver! */
496497
virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
497498

498-
ret = virtio_finalize_features(dev);
499+
ret = dev->config->finalize_features(dev);
500+
if (ret)
501+
goto err;
502+
503+
ret = virtio_features_ok(dev);
499504
if (ret)
500505
goto err;
501506

include/linux/virtio_config.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ struct virtio_shm_region {
6464
* Returns the first 64 feature bits (all we currently need).
6565
* @finalize_features: confirm what device features we'll be using.
6666
* vdev: the virtio_device
67-
* This gives the final feature bits for the device: it can change
67+
* This sends the driver feature bits to the device: it can change
6868
* the dev->feature bits if it wants.
69+
* Note: despite the name this can be called any number of times.
6970
* Returns 0 on success or error status
7071
* @bus_name: return the bus name associated with the device (optional)
7172
* vdev: the virtio_device

0 commit comments

Comments
 (0)