ivila commented on issue #153:
URL: 
https://github.com/apache/incubator-teaclave-trustzone-sdk/issues/153#issuecomment-2522064909

   @DemesneGH 
   ## For the questions:
   * Should developers care about the shared buffer?
       * Yes, not just my cases, in many of the use cases the response size is 
bigger than request size, and actually they are not really care about the 
shared buffer, but just want to make sure they can read all the data from REE.
   * The plugin_invoke() takes these two parameters data_ptr and data_len. they 
are description of the shared_buffer, or the data_len is size of input_buffer 
(as the comment for tee_invoke_supp_plugin)?
       * Yes, the `len` is the size of share buffer, **But we don't need to 
consider the size of the input data in teaclave**, things is complicated here, 
I will explain later.
       * **Developers should manually set the input_data size this their own**
   * Would zero-copy of Vec in Rust solve the problem?
       * Yes, but as what I said, my real problem is not we can't copy, is that 
I need to set the size of the buffer my own but rather than use a immutable 
input slice with same size as the output Vector(**in my case it wastes 100M of 
memory by using invoke**).
   
   ## How people use plugin with the share buffer
   ### 1. Read data from REE
   Some people use it to read file content, or make a request to remote and 
return its result, in scenarios like these, people prepare a large buffer and 
use different ways to know the input data size from TA:
   1. by const size: every command is combine with a const struct, which is the 
same layout in both TEE and REE (just like repr(C)), and they read the buffer 
directly, when upgrading, they use a new struct with new command pair(new 
command_id or same command_id with different sub_cmd_id).
   2. by sub_cmd: some people use protobuf or json or flatbuffer for request 
parameter, and use sub cmd for lens.
   3. by first 8 bytes: they write the length (which is u64) to the prefix of 
buffer.
   
   ### 2. Post something to REE
   Some people use it to write data into REE, for example, some secret keys, 
and the share buffer is used to pass the error description(if result is not 
TEE_SUCCESS), in scenarios like these, current implementation is enough.
   
   ### 3. Trigger something in REE
   Some people use it to trigger something in REE, for example, TEE listening 
to a trusty remote, and when the maintainers find something wrong with it (a 
hacker hack in and take control of the system), they trigger a security alarm, 
or shutdown the system immediately, etc. In scenarios like these, they don't 
care about the share buffer, sometimes they don't use the sub_command_id either.
   
   ## Other considerations
   * Actually plugin system have existed for a long time, there are some 
plugins developed by C already, so should we keeps compatibility with them?
       * Yes? => So people don't have to rewrite them.
       * No? => So we can keep a clean architecture.
   
   ## My New Design
   I have a new design, which is Command struct with chainable api, just like 
[reqwest.Request](https://docs.rs/reqwest/latest/reqwest/struct.Request.html) 
and [ureq.Request](https://docs.rs/ureq/latest/ureq/struct.Request.html).
   
   First, we propose a Command struct, reference to the Plugin and keeps 
arguments of tee_invoke_supp_plugin inside:
   ```rust
   pub struct Plugin {
       uuid: Uuid,
   }
   
   pub struct Command<'a> {
       plugin: &'a Plugin,
       cmd_id: u32,
       sub_cmd_id: u32,
       buffer: Vec<u8>,
   }
   ```
   Then, we provide chainable api for developers, just like the following:
   ```rust
   impl<'a> Command<'a> {
       // use this to set sub_cmd if needed
       pub fn sub_cmd(mut self, sub_cmd_id: u32) -> Self {
           self.sub_cmd_id = sub_cmd_id;
           self
       }
       // use this to write request body if needed
       pub fn write_body(&mut self, data: &[u8]) {
           self.buffer.extend_from_slice(data);
       }
       // same with write_body, but chainable
       pub fn chain_write_body(mut self, data: &[u8]) -> Self {
           self.write_body(data);
           self
       }
       // invoke the command, and get result from it
       pub fn call(self) -> Result<Vec<u8>> {
           let mut outlen: usize = 0;
           let mut buffer = self.buffer;
           buffer.resize(buffer.capacity(), 0); // resize to capacity first
           match unsafe {
               raw::tee_invoke_supp_plugin(
                   self.plugin.uuid.as_raw_ptr(),
                   self.cmd_id,
                   self.sub_cmd_id,
                   buffer.as_mut_slice().as_mut_ptr(),
                   buffer.len(),
                   &mut outlen as *mut usize,
               )
           } {
               raw::TEE_SUCCESS => {
                   assert!(outlen <= buffer.len());
                   buffer.resize(outlen, 0);
                   Ok(buffer)
               }
               code => Err(Error::from_raw_error(code)),
           }
       }
   }
   ```
   Finally, we add extra api for Plugin, and rewrite the invoke method with it:
   ```rust
   impl<'a> Command<'a> {
       fn new(plugin: &'a Plugin, cmd_id: u32) -> Self {
           const DEFAULT_CAPACITY: usize = 1024;
           Self::new_with_capacity(&plugin, cmd_id, DEFAULT_CAPACITY)
       }
       fn new_with_capacity(plugin: &'a Plugin, cmd_id: u32, capacity: usize) 
-> Self {
           Self {
               plugin,
               cmd_id,
               sub_cmd_id: 0,
               buffer: Vec::with_capacity(capacity),
           }
       }
   }
   
   impl Plugin {
       pub fn invoke(&self, command_id: u32, subcommand_id: u32, data: &[u8]) 
-> Result<Vec<u8>> {
           self.new_command_with_capacity(command_id, data.len())
               .sub_cmd(subcommand_id)
               .chain_write_body(data)
               .call()
       }
       // for people don't care about the response
       pub fn new_command<'a>(&'a self, command_id: u32) -> Command<'a> {
           Command::new(self, command_id)
       }
       // for people who wants to set the buffer size for response manually
       pub fn new_command_with_capacity<'a>(
           &'a self,
           command_id: u32,
           capacity: usize,
       ) -> Command<'a> {
           Command::new_with_capacity(self, command_id, capacity)
       }
   }
   ```
   By this design, developers of different scenarios could:
   1. developers upgrade from old teaclave SDK: nothing changes.
   2. developers who wants to fetch something from REE(which have bigger 
response size): 
   ``` rust
   const RESPONSE_LEN: usize = 100;
   let result_vec = plugin.new_command_with_capacity(command_id, RESPONSE_LEN)
       .sub_cmd(subcommand_id)
       .write_body(request)
       .call()?;
   ```
   3. developers who wants to put something to REE: just use the invoke method.
   4. developers who just want to trigger something:
   ```rust
   let _  = plugin.new_command(command_id)
       .sub_cmd(subcommand_id)
       .write_body(request)
       .call()?;
   ```
   5. they can even imply a writer of it:
   ```rust
   // impl a writer for Command
   struct Wrapper<'a, 'b>(&'b mut Command<'a>);
   impl<'a, 'b> std::io::Write for Wrapper<'a, 'b> {
       fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
           self.0.write_body(buf);
           Ok(buf.len())
       }
       fn flush(&mut self) -> std::io::Result<()> {
           Ok(())
       }
   }
   // serialize data into command directly
   let test_data = serde_json::json!({
       "code": 100,
       "message": "error"
   });
   const OUTPUT_LEN: usize = 100;
   let (sub_cmd, random) = generate_test_pair(OUTPUT_LEN);
   let mut cmd = plugin
       .new_command_with_capacity(1, OUTPUT_LEN)
       .sub_cmd(sub_cmd);
   serde_json::to_writer(Wrapper(&mut cmd), &test_data).unwrap();
   let result_vec = cmd.call().unwrap();
   ```
   How about this?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@teaclave.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@teaclave.apache.org
For additional commands, e-mail: dev-h...@teaclave.apache.org

Reply via email to