Skip to content
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

fix(rumqtt): validate topic and topic filter #813

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

arunanshub
Copy link
Contributor

@arunanshub arunanshub commented Mar 6, 2024

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • Formatted with cargo fmt
  • Make an entry to CHANGELOG.md if it's relevant to the users of the library. If it's not relevant mention why.

Fixes #595

@arunanshub arunanshub marked this pull request as draft March 6, 2024 10:51
@swanandx
Copy link
Member

swanandx commented Mar 6, 2024

note that it won't fix #595 as broker still doesn't perform the checks, and doing that in separate PR is preferable. I would recommend to remove that from PR description so it won't automatically close the issue if this PR is merged.

@arunanshub
Copy link
Contributor Author

arunanshub commented Mar 6, 2024

note that it won't fix #595 as broker still doesn't perform the checks

It is still an ongoing work.

Since broker will perform the same task, I will implement it in this PR itself.

@coveralls
Copy link

coveralls commented Mar 6, 2024

Pull Request Test Coverage Report for Build 8519279552

Details

  • 45 of 164 (27.44%) changed or added relevant lines in 13 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall first build on fix/validate-topic at 36.493%

Changes Missing Coverage Covered Lines Changed/Added Lines %
rumqttc/src/v5/mqttbytes/mod.rs 20 21 95.24%
rumqttd/src/protocol/v4/publish.rs 0 3 0.0%
rumqttd/src/protocol/v4/unsubscribe.rs 0 3 0.0%
rumqttd/src/protocol/v5/publish.rs 0 3 0.0%
rumqttd/src/protocol/v5/subscribe.rs 0 3 0.0%
rumqttd/src/protocol/v5/unsubscribe.rs 0 3 0.0%
rumqttc/src/v5/mqttbytes/v5/subscribe.rs 0 4 0.0%
rumqttd/src/protocol/v4/subscribe.rs 0 4 0.0%
rumqttc/src/mqttbytes/v4/subscribe.rs 0 5 0.0%
rumqttd/src/protocol/mod.rs 1 28 3.57%
Totals Coverage Status
Change from base Build 8432798356: 36.5%
Covered Lines: 6017
Relevant Lines: 16488

💛 - Coveralls

@arunanshub arunanshub marked this pull request as ready for review March 14, 2024 09:50
rumqttc/src/client.rs Outdated Show resolved Hide resolved
rumqttc/src/client.rs Outdated Show resolved Hide resolved
rumqttc/src/client.rs Outdated Show resolved Hide resolved
rumqttc/src/mqttbytes/topic.rs Outdated Show resolved Hide resolved
Comment on lines +56 to +59
(is_topic_valid || is_topic_empty)
&& (!is_topic_empty || is_alias_given)
&& (!is_alias_given || is_alias_valid)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo this is harder to read in comparison to if...else branch, wdyt @swanandx?

Copy link
Contributor Author

@arunanshub arunanshub Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some investigation. It seems that apart from the length of the function, the assembly for if-else version of validate_topic_name_and_alias remains the same. How should we proceed?

https://godbolt.org/z/WPrc4P66s

Copy link
Contributor

@flxo flxo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution.

Is there a benchmark somewhere? The topic validation is in the hot path and must not have any crucial performance impact.

@arunanshub arunanshub mentioned this pull request Mar 28, 2024
2 tasks
@arunanshub
Copy link
Contributor Author

Hello @flxo. The hot paths that you mention use generic functions (eg. impl AsRef<str>). According to this discussion on Reddit, in release builds the generic functions are inlined. Furthermore, according to matklad on his blog, private functions are always inlined. I believe that all of these factors should help with performance.

Also feel free to review the changes and suggest any scope of improvements, should you happen to find any.

@arunanshub arunanshub requested a review from h3nill March 28, 2024 17:08
@flxo
Copy link
Contributor

flxo commented Apr 2, 2024

Hello @flxo. The hot paths that you mention use generic functions (eg. impl AsRef<str>). According to this discussion on Reddit, in release builds the generic functions are inlined. Furthermore, according to matklad on his blog, private functions are always inlined. I believe that all of these factors should help with performance.

Sure - this all helps. I'm just curious if someone used e.g criterion to verify that best effort is done.

Also feel free to review the changes and suggest any scope of improvements, should you happen to find any.

Sure.

rumqttc/src/mqttbytes/topic.rs Outdated Show resolved Hide resolved
@@ -16,8 +37,9 @@ pub fn valid_topic(topic: &str) -> bool {
/// Checks if the filter is valid
///
/// <https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106>
pub fn valid_filter(filter: &str) -> bool {
if filter.is_empty() {
pub fn valid_filter(filter: impl AsRef<str>) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd name this is_filter_valid

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is a public function, we may run into the risk of backwards incompatibility due to name change. What do you think?

rumqttc/src/v5/client.rs Outdated Show resolved Hide resolved
rumqttd/src/protocol/mod.rs Outdated Show resolved Hide resolved
rumqttd/src/protocol/mod.rs Show resolved Hide resolved
rumqttd/src/protocol/mod.rs Outdated Show resolved Hide resolved
@@ -34,6 +35,10 @@ pub fn read(
bytes.advance(variable_header_index);
let topic = read_mqtt_bytes(&mut bytes)?;

if !valid_topic(from_utf8(&topic).map_err(|_| Error::TopicNotUtf8)?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the validation is done (again) when obtaining len. This can be a problem when len is used during encoding since the utf8 check is not cheap at all.

Have you thought about a TryFrom impl for all the packet types in here? Validate the parameters once at construction time (and maybe precalculate the length once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Empty strings are considered as valid topic
5 participants