-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added support for Matter OTA firmware #815
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, aside from the noted sanity checks that should be performed to prevent crashes/exceptions in the event of malformed/corrupted data. Once those are addressed, I'll merge the PR. Thanks!
let total_header_size = | ||
MAGIC_SIZE + TOTAL_SIZE_SIZE + HEADER_SIZE_SIZE + ota_header.header_size; | ||
|
||
result.size = Some(ota_header.total_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By convention internal extractors should set result.success = true
here, so that if the extractor will still return success if it is invoked as part of a dry-run (i.e., output_directory
is None
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result.success is now set to true even when there is a dry run
src/structures/matter_ota.rs
Outdated
// Header starts after the magic, total size and header size fields | ||
let header_start = common::size(&ota_structure); | ||
let header_end = header_start + header_size; | ||
let header_data = &ota_data[header_start..header_end]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no guarantee that the values reported in the ota_stucture
are sane, thus the calculated header_start
/ header_end
values could cause a crash here. Consider using ota_data.get()
and/or sanity checking the header_start
and header_end
values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this to using ota_data.get()
src/structures/matter_ota.rs
Outdated
_ => return Err(StructureError), | ||
}; | ||
|
||
let field_data = &data[field_offset..]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although field_offset
is a small value, there does not appear to be any guarantee that data
is at least field_offset
bytes in size. Consider using data.get()
or sanity checking the size of data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now use data.get() here as well.
src/structures/matter_ota.rs
Outdated
let structure = &vec![("string_length", field_width_type)]; | ||
let result = common::parse(field_data, structure, "little")?; | ||
let string_length = result["string_length"] as usize; | ||
let string_data = &field_data[common::size(structure)..]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using field_data.get()
or sanity checking the size of field_data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using field_data.get() here.
src/structures/matter_ota.rs
Outdated
let result = common::parse(field_data, structure, "little")?; | ||
let string_length = result["string_length"] as usize; | ||
let string_data = &field_data[common::size(structure)..]; | ||
let string = get_cstring(&string_data[..string_length]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using string_data.get()
or sanity checking the size of string_data
. Alternatively, you can simply pass string_data
directly to get_cstring()
, as it will terminate at the first null byte or the end of data, whichever comes first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TLV encoded strings aren't null-terminated in the headers I've seen. But I changed to using string_data.get() here as well
src/structures/matter_ota.rs
Outdated
let structure = &vec![("octet_string_length", field_width_type)]; | ||
let result = common::parse(field_data, structure, "little")?; | ||
let octet_string_length = result["octet_string_length"] as usize; | ||
let octet_string_data = &field_data[common::size(structure)..]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using field_data.get()
or sanity checking the size of field_data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using field_data.get() now
src/structures/matter_ota.rs
Outdated
Ok(( | ||
Element { | ||
tag, | ||
value: Value::OctetString(octet_string_data[..octet_string_length].to_vec()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using octet_string_data.get()
or sanity checking the size of octet_string_data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using octet_string_data.get() here
src/structures/matter_ota.rs
Outdated
]; | ||
let mut offset = 0; | ||
let mut header = HashMap::new(); | ||
while offset < data.len() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider sanity checking the offset
value by using while common::is_offset_safe()
here to prevent infinite loops, wrap-arounds, and out-of-bounds access.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed to using common::is_offset_safe()
Thanks for your review comments!
This PR adds support for detecting Matter OTA firmware (https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/protocols/matter/overview/dfu.html#matter_ota_image_structure_and_transfer) and also extracting the firmware payload itself.
Example output for the file https://prod.ota-updates.bosch-smarthome.com/v1/updates/BTH-RAD/BTH-RAD_Prod_2.0.9.ota found via the DCL UI at https://webui.dcl.csa-iot.org/models
The test file in tests/inputs has been created manually with a dummy payload.