On the project I'm working on we are parsing MAVLink messages from PX4 and QGroundControl, intercepting some and forwarding the rest. While doing that I've noticed that some forwarded messages containing char[] fields are not byte equivalent before and after decode and re-encoding.
The MAVLink spec doesn't specify anything about NULL termination and each message seems to have its own specific requirements around it. Some examples:
- AUTH_KEY (7):
key: char[32] nothing specified. No null-termination as the key would always be 32 chars long.
- PARAM_VALUE (22):
param_id: char[16] terminated by NULL if the length is less than 16 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 16 chars
- ADSB_VEHICLE (246):
callsign: char[9] 8+null
- STATUSTEXT (253):
text: char[50] Status text message, without null termination character
It is not clear to me if ALL strings that are smaller than the char[] field length should be null-terminated or not?
In any case rust-mavlink does not handle this correctly. If we look at the PARAM_VALUE_DATA struct that is generated:
pub struct PARAM_VALUE_DATA {
// omitted some fields...
pub param_id: arrayvec::ArrayString<16>,
// omitted some fields...
}
In the deserialization code we can see that if a null byte is encountered it will be truncated from the string.
for _ in 0..16usize {
let next_char = buf.get_u8();
if next_char == 0 {
break;
}
__struct.param_id.push(next_char as char);
}
In the serialization however we are only adding values for as many bytes are in the slice. First this is bad because it means if the array/string does not contain exactly the amount of bytes that the field is expecting we are shifting everything left. Second when re-encoding the message we received from the deserialization step we've already lost the null-byte if it was there and thus we are always triggering the issue.
let slice = self.param_id.as_bytes();
for val in slice {
__tmp.put_u8(*val);
}
Here is an example of the PARAM_VALUE message before deserialization and after deserialization + re-serialization in wireshark. You can see the null byte is missing and the length is one shorter.
I can take a stab at fixing this, but I would first like to discuss what the right solution would be. Would the simple solution of zero filling the remaining bytes be the correct solution for all cases?
On the project I'm working on we are parsing MAVLink messages from PX4 and QGroundControl, intercepting some and forwarding the rest. While doing that I've noticed that some forwarded messages containing
char[]fields are not byte equivalent before and after decode and re-encoding.The MAVLink spec doesn't specify anything about NULL termination and each message seems to have its own specific requirements around it. Some examples:
key: char[32]nothing specified. No null-termination as the key would always be 32 chars long.param_id: char[16]terminated by NULL if the length is less than 16 human-readable chars and WITHOUT null termination (NULL) byte if the length is exactly 16 charscallsign: char[9]8+nulltext: char[50]Status text message, without null termination characterIt is not clear to me if ALL strings that are smaller than the
char[]field length should be null-terminated or not?In any case rust-mavlink does not handle this correctly. If we look at the
PARAM_VALUE_DATAstruct that is generated:In the deserialization code we can see that if a null byte is encountered it will be truncated from the string.
In the serialization however we are only adding values for as many bytes are in the slice. First this is bad because it means if the array/string does not contain exactly the amount of bytes that the field is expecting we are shifting everything left. Second when re-encoding the message we received from the deserialization step we've already lost the null-byte if it was there and thus we are always triggering the issue.
Here is an example of the
PARAM_VALUEmessage before deserialization and after deserialization + re-serialization in wireshark. You can see the null byte is missing and the length is one shorter.I can take a stab at fixing this, but I would first like to discuss what the right solution would be. Would the simple solution of zero filling the remaining bytes be the correct solution for all cases?