Skip to content

Unsafe Tensor::from_blob borrows stack/buffer data with no lifetime guarantee #38

@CarsonBurke

Description

@CarsonBurke

Summary

Tensor::from_blob creates a non-owning tensor that wraps a raw pointer to slice data. If the tensor outlives its source data (e.g., source is mutated or freed), this is use-after-free / data corruption.

Locations

  • trading_bots/src/torch/ppo.rs:105-115cpu_tensor_from_f32 free function
  • trading_bots/src/torch/env/vec_env.rs:60-69VecEnv::tensor_from_f32 method

Analysis

Most call sites immediately consume the non-owning tensor via .copy_(), which is safe in practice:

  • ppo.rs:468-480 — all four uses pass the result directly to .copy_()
  • vec_env.rs:497-506 (in step_into_ring) — all uses pass to .copy_()

However, vec_env.rs:232-233 (in step_into) returns the non-owning tensors as (reward, is_done). These reference self.reward_buf and self.is_done_buf which are long-lived Vec fields, so the backing memory is stable — but the data can be silently overwritten on the next step_into or step_into_ring call. This is safe only as long as the caller processes these tensors before the next env step.

The pattern is fragile and relies on implicit ordering guarantees. Any future refactor that defers consumption of the returned tensors would introduce silent data corruption with no compiler warning.

Suggested Fix

Replace Tensor::from_blob with Tensor::from_slice() (or Tensor::of_slice()) which copies and owns the data. The performance cost is negligible for these small CPU tensors (NPROCS-sized buffers).

Severity

Low — currently safe due to immediate consumption patterns, but fragile and unsafe-block-dependent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: lowLow severity bug

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions