[tor-bugs] #24031 [Core Tor/Tor]: Protover.rs could use a better algorithm
Tor Bug Tracker & Wiki
blackhole at torproject.org
Mon Mar 26 18:48:30 UTC 2018
#24031: Protover.rs could use a better algorithm
-------------------------------------------------+-------------------------
Reporter: nickm | Owner: isis
Type: defect | Status:
| needs_review
Priority: Very High | Milestone: Tor:
| 0.3.3.x-final
Component: Core Tor/Tor | Version: Tor:
| 0.3.3.1-alpha
Severity: Normal | Resolution:
Keywords: rust, 033-must, protover, security, | Actual Points: 5
033-triage-20180326, 033-included-20180326 |
Parent ID: | Points: 1
Reviewer: nickm | Sponsor:
| SponsorM-can
-------------------------------------------------+-------------------------
Changes (by isis):
* status: needs_revision => needs_review
Comment:
Replying to [comment:15 chelseakomlo]:
> Hi Isis,
>
> This looks great! Thanks for doing this and making these improvements,
it is much cleaner!
Hey! Thanks for the review!
> There are a few things I noticed when looking at this, they are mostly
nits. I'll do a deeper look by the end of this week, but this is looking
significantly better.
>
> 1. If possible, it would be ideal to keep all FFI/C related code in
ffi.rs. This way we can keep Rust/C code cleanly separated and if Rust
needs to use these functions in the future, we don't need to do any
refactoring or translation (for example, compute_for_old_tor and
get_supported_protocols). It looks like in at least once place we get a
string as a cstring in Rust (in supported for ProtoEntry) and then
translate into a string- we should just keep this all as strings in Rust
and then only translate in ffi.rs when we actually want to send something
to C.
So the reasoning for this is that static strings should live in the module
which owns them. (This code is from #25185.) Since we needed a generalised
way to work with static strings between both C and Rust without
(re)allocations, in that ticket I created the `cstr!` macro, but a caveat
is that it ''must'' take a string literal in order to create the `CStr`
(`&str` is to `String` as `&CStr` is to `CString`, i.e. the static, not-
heap-allocated equivalent), meaning that we can't assign it to a variable
first and then put it into the macro elsewhere. E.g. it ''would'' be nicer
if the `protover::get_supported_protocols_cstr()` function actually lived
in the `protover::ffi` module, but then we'd need to call something from
`protover::ffi` in the main `protover.rs` module, and that seemed way
backwards to me?
So for the `ProtoEntry::supported()` method, it internally gets the
`&CStr` from `protover::get_supported_protocols_cstr()` and immediately
calls `.to_str()` on it so that we're working with the underlying `&str`
in Rust, then it parses that.
Basically, the problem is that the macro needs to actually have literally
the `&static str` ''inside'' it, it cannot work at compile-time if the
`&static str` and the call to the macro live in different places, because
at compile-time when the macro is expanded there isn't yet a symbol table
available for looking up the `&static str` if it were to live somewhere
else.
> 2. I really like how you broke out errors into their own file. Is this
something we should add to our guide to writing Rust in Tor?
Probably! I was going to draft it into a guideline, but this will also
become much better when we can use [https://crates.io/crates/failure
failure] which is supposed to be 1.0.0 any day now. Adding it for
`ProtoverError` is a one line addition:
{{{#!rust
impl ::failure::Fail for ProtoverError {};
}}}
I've made #25628 for tracking this.
> 3. Is generating ToString implementations at runtime a common Rust
practice? If not, document impl_to_string_for_proto_entry
It's compile-time! :) To see what it's generating, add this to the top of
`lib.rs` (and use a nightly compiler):
{{{#!rust
#![feature(trace_macros)]
trace_macros!(true);
}}}
I don't know if it's standard to do it for `ToString`, but people
generally use macros to generate trait implementations for types that
should have the same implementation to avoid copy-pasting code that could
get out of sync. (I see people do it a lot for `Display`/`Debug`/`Clone`,
etc.)
> 4. `supported` for ProtoEntry maybe should return an actual error
instead of an empty string
It is! `ProtoEntry::supported()` returns `ProtoverError` because it sets
the `supported` variable to an empty string if there was an error, and
then returns `supported.parse()`.
{{{#!rust
impl ProtoEntry {
// [...]
pub fn supported() -> Result<Self, ProtoverError> {
let supported_cstr: &'static CStr =
get_supported_protocols_cstr();
let supported: &str = match supported_cstr.to_str() {
Ok(x) => x,
Err(_) => "",
};
supported.parse()
}
}
}}}
(`supported.parse()` is calling `impl FromStr for ProtoEntry`, and a blank
string is not a valid `ProtoEntry` as the
`test_protoentry_from_str_empty()` shows.)
Do you think it might read better if I refactored setting `supported` to
be like this?
{{{#!rust
let supported: &str = supported_cstr.to_str().unwrap_or("");
}}}
-----
Setting as needs_review again? Not sure what the correct ticket state is?
Feel free to change it back if you still think revision is necessary
somewhere!
--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/24031#comment:17>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online
More information about the tor-bugs
mailing list