block: qcow: Validate QCOW2 v3 incompatible feature bits - #7612
Conversation
0efb9ee to
c174a63
Compare
phip1611
left a comment
There was a problem hiding this comment.
Thanks for your contribution - it's good to have a broader safety net for users. I left a comment; otherwise, LGTM.
fc04678 to
cf00efa
Compare
There was a problem hiding this comment.
Looking good, I however think we can simplify the code and improve the error reporting even further. My main concern: #7612 (comment)
In case I'm missing something or you can't use bitflags for some reason, feel free to reach out to me again!
| @@ -323,6 +366,30 @@ impl QcowHeader { | |||
| Ok(()) | |||
| } | |||
|
|
|||
| fn report_unsupported_features(unsupported: u64, feature_table: &[(u8, String)]) -> Error { | |||
| let features: Vec<String> = (0u8..64) | |||
There was a problem hiding this comment.
with bitflags, this would become let features = FeatureBits::from_bits_retain()
cf00efa to
5222ccd
Compare
5222ccd to
7f1cd6e
Compare
There was a problem hiding this comment.
Almost there! Just a few more nits to integrate this nicely with the existing error infrastructure (std::err::Error).
It would be also great if you can run CHV where you artificially let Cloud Hypervisor fail with this error so that we can see the error chain that this produces. Similar to this but with your new error type:
cloud-hypervisor --kernel /etc/bootitems/linux/kernel_minimal/stable.bzImage --cmdline "console=ttyS0" --serial tty --console off --initramfs /etc/bootitems/linux/initrd_minimal/default --firmware a
cloud-hypervisor: 0.001595s: <main> ERROR:/build/source/cloud-hypervisor/src/lib.rs:23 -- Fatal error: ParsingConfig(Validation(PayloadError(FirmwarePlusOtherPayloads)))
Error: Cloud Hypervisor exited with the following chain of errors:
0: Error parsing config
1: Error validating configuration
2: Payload configuration is not bootable
3: Specifying a kernel is not supported when a firmware is provided
| @@ -116,6 +117,8 @@ pub enum Error { | |||
| UnsupportedBackingFileFormat(String), | |||
| #[error("Unsupported compression type")] | |||
| UnsupportedCompressionType, | |||
| #[error("Unsupported qcow2 feature(s): {0}")] | |||
There was a problem hiding this comment.
| #[error("Unsupported qcow2 feature(s): {0}")] | |
| #[error("Unsupported qcow2 feature(s)")] |
this is okay as if we have a proper std::err::Error impl, we will print the underlying error as part of the error chain when Cloud Hypervisor fails
|
|
||
| /// Error type for unsupported incompatible features. | ||
| #[derive(Debug, Clone)] | ||
| pub struct MissingFeatureError { |
There was a problem hiding this comment.
missing impl of std::err:Error. You should derive thiserror::Error here. Look at other types in the repo that do similar things
There was a problem hiding this comment.
Using #[derive(Error)] isn't possible here because the Display impl has runtime logic. All existing usages of Error seem to have static error messages, though. Thus, I went for the manual impl which keeps the structured data while participating in the error chain.
Thanks
There was a problem hiding this comment.
Actually, I initially misremembered that #[derive(Error)] requires the #[error("...")] attribute to generate Display. But thiserror seems to detect if a Display impl already exists. So the separate impl std::error::Error was unnecessary. Fixed as suggested, thanks.
7f1cd6e to
766f232
Compare
|
@phip1611 thanks for the further hints! That's where you learn about the error hierarchy when you thought you already knew it :) Following your sugestion, here's some quick bash to run qemu-img create -f qcow2 /tmp/test-incompat.qcow2 8M
python3 -c "import struct; f=open('/tmp/test-incompat.qcow2','r+b'); f.seek(72); f.write(struct.pack('>Q', (1<<2)|(1<<4))); f.close()"
cargo build
./target/debug/cloud-hypervisor --kernel /dev/null --disk path=/tmp/test-incompat.qcow2With the output shown I see the Rust manual suggests an error message to be lower case. I just made "Missing" capitalized to match the project style. Thanks |
766f232 to
68ce97e
Compare
Lovely! Thanks, great work!
Yup, that's a Cloud Hypervisor thingy we have. Thanks! |
phip1611
left a comment
There was a problem hiding this comment.
Good job, thanks for your contribution
Parse the feature name table header extension to provide descriptive error messages when unsupported incompatible features are detected. Currently only the compression bit (bit 3, zstd) is supported. This prevents opening qcow2 images with features that would cause incorrect behavior or data corruption (e.g., dirty bit, corrupt bit, external data file, extended L2 entries). Feature names are defined as follows: 1. The image's feature name table header extension (if present) 2. Hardcoded fallback names for known features 3. Generic "unknown feature bit N" for undefined features Signed-off-by: Anatol Belski <[email protected]> Co-developed-by: Philipp Schuster <[email protected]>
Add test cases verifying QCOW2 v3 images with unsupported incompatible feature bits are correctly rejected. Signed-off-by: Anatol Belski <[email protected]>
68ce97e to
cc68557
Compare
|
Thanks @phip1611 for the thorough review and guidance on the error handling! Added you as Co-developed-by to this one for driving the error infrastructure integration. |
e61901d
Add validation for QCOW2 v3 incompatible feature bits, rejecting images that use features not supported by Cloud Hypervisor. Currently only compression (bit 3) is supported.
Till now, images with unsupported features (dirty, corrupt, external data file, extended L2) could be opened without error, potentially causing incorrect behavior or data corruption.
When unsupported features are detected, the feature name table header extension is parsed to provide descriptive error messages. The name table is only read when validation fails, avoiding overhead for valid images.