[tor-bugs] #23881 [Core Tor/Tor]: Implement a way to utilise tor's logging system from Rust code

Tor Bug Tracker & Wiki blackhole at torproject.org
Tue Feb 27 20:36:49 UTC 2018


#23881: Implement a way to utilise tor's logging system from Rust code
-----------------------------------------------+---------------------------
 Reporter:  isis                               |          Owner:  isis
     Type:  enhancement                        |         Status:  accepted
 Priority:  High                               |      Milestone:  Tor:
                                               |  0.3.3.x-final
Component:  Core Tor/Tor                       |        Version:
 Severity:  Normal                             |     Resolution:
 Keywords:  rust, rust-pilot, review-group-29  |  Actual Points:
Parent ID:                                     |         Points:  3
 Reviewer:  nickm                              |        Sponsor:
-----------------------------------------------+---------------------------

Comment (by isis):

 Okay, to answer the question of "Does `CString::new` perform a copy?"

 Manish answered above, but I ''think'' there was a typo in the answer? (It
 seems to be missing the word "not" in the latter clause?) I had assumed it
 always copied, so I went and dug through the stdlib to figure it out.

 So [https://doc.rust-lang.org/src/std/ffi/c_str.rs.html#330-332 looking at
 the source of `CString::new`]:

 {{{#!rust
     pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> {
         Self::_new(t.into())
     }

     fn _new(bytes: Vec<u8>) -> Result<CString, NulError> {
         match memchr::memchr(0, &bytes) {
             Some(i) => Err(NulError(i, bytes)),
             None => Ok(unsafe { CString::from_vec_unchecked(bytes) }),
         }
     }
 }}}

 [wow!!! check out that ↑↑↑↑↑↑ frickin syntax highlighting!!!!! zomg, my
 life is changed!!]

 it takes a any `T` which has implemented a way (`Into<T>`) to transmute it
 into a `Vec<u8>` and calls [https://doc.rust-
 lang.org/src/std/ffi/c_str.rs.html#334-339 `Cstring::_new()`] on it. There
 [https://doc.rust-lang.org/src/alloc/vec.rs.html#2226-2230 exists]
 `impl<'a> From<&'a str> for Vec<u8>`:

 {{{#!rust
 impl<'a> From<&'a str> for Vec<u8> {
     fn from(s: &'a str) -> Vec<u8> {
         From::from(s.as_bytes())
     }
 }
 }}}

 The `_new()` function checks that is has no NUL bytes, and errors if it
 does, then calls [https://doc.rust-
 lang.org/src/std/ffi/c_str.rs.html#361-365
 `CString::from_vec_unchecked()`]:

 {{{#!rust
     pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString {
         v.reserve_exact(1);
         v.push(0);
         CString { inner: v.into_boxed_slice() }
     }
 }}}

 And then [https://doc.rust-
 lang.org/alloc/vec/struct.Vec.html#method.into_boxed_slice
 `Vec::into_boxed_slice()`]:

 {{{#!rust
     pub fn into_boxed_slice(mut self) -> Box<[T]> {
         unsafe {
             self.shrink_to_fit();
             let buf = ptr::read(&self.buf);
             mem::forget(self);
             buf.into_box()
         }
     }
 }}}

 which is doing some olympic level gymnastics where [https://doc.rust-
 lang.org/std/ptr/fn.read.html `std::ptr::read`] is returning a `Rawvec`,
 then it `mem::forget()`s itself so that the deallocator never runs, and
 finally it `Box`es the memory it forgot about by calling
 `RawVec::into_box()`:

 {{{#!rust
 impl<T> RawVec<T, Heap> {
     /// Converts the entire buffer into `Box<[T]>`.
     ///
     /// While it is not *strictly* Undefined Behavior to call
     /// this procedure while some of the RawVec is uninitialized,
     /// it certainly makes it trivial to trigger it.
     ///
     /// Note that this will correctly reconstitute any `cap` changes
     /// that may have been performed. (see description of type for
 details)
     pub unsafe fn into_box(self) -> Box<[T]> {
         // NOTE: not calling `cap()` here, actually using the real `cap`
 field!
         let slice = slice::from_raw_parts_mut(self.ptr(), self.cap);
         let output: Box<[T]> = Box::from_raw(slice);
         mem::forget(self);
         output
     }
 }
 }}}

 which is doing even more olympic level gymnastics with [https://doc.rust-
 lang.org/std/slice/fn.from_raw_parts.html `slice::from_raw_parts`], which
 is like the King of Capital-U Unsafety, because it just says "N bytes from
 this pointer forward are now a slice, with an lifetime that I decided by
 royal proclamation, and which is not necessarily the correct lifetime
 given that I was handed a raw pointer that I know nothing about". But it
 doesn't copy. Then it does a similar pirouette as the trick we saw before
 with `std::ptr::read` and then `Box`ing the `RawVec`, but even more
 complicated and dangerous, like if the last one was a pirouette, this is
 like a pirouette while juggling machetes. `Box::from_raw` is only
 ''really'' supposed to be called on pointers produced via `Box::into_raw`,
 but whatever, so many crazy things have already been done with these
 pointers to this memory. So `Box::from_raw` creates a [https://doc.rust-
 lang.org/std/ptr/struct.Unique.html `Unique<T>`], which just says, "I own
 this raw pointer to a `T` (`*mut T`), and I'm just going to pretend it
 ''is'' a `T`." and then calls `Box::from_unique` on the resulting
 `Unique<T>`:

 {{{#!rust
     pub unsafe fn from_unique(u: Unique<T>) -> Self {
         mem::transmute(u)
     }
 }}}

 which, as you can see, finishes its machete-juggling pirouette off with a
 triple axel flamethrower party by calling `mem::transmute`, re-
 interpretting the raw bits of the wrapped raw pointer as a `Box`. So much
 Unsafe! Seemingly no additional allocations. :)

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/23881#comment:36>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list