Skip to content

Contain enable_profiling path to model directory (path traversal hardening)#2273

Open
jiafatom wants to merge 2 commits into
mainfrom
fix/enable-profiling-path-containment
Open

Contain enable_profiling path to model directory (path traversal hardening)#2273
jiafatom wants to merge 2 commits into
mainfrom
fix/enable-profiling-path-containment

Conversation

@jiafatom

@jiafatom jiafatom commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the enable_profiling field of genai_config.json against path traversal (CWE-22).

The field was read from the (untrusted) model config and passed verbatim to OnnxRuntime's EnableProfiling():

// src/models/model.cpp
fs::path profile_file_prefix{config_session_options.enable_profiling.value()};
session_options.EnableProfiling(profile_file_prefix.c_str());  // honored verbatim

ORT writes a <prefix>_<timestamp>.json profiling file when the session closes. A malicious model package could set e.g. "enable_profiling": "../../../../etc/cron.d/ort" (Linux) or a ...\Startup\evil path (Windows) to write to arbitrary locations.

Fix

  • src/models/model.cpp: add ResolveContainedConfigPath, which rejects absolute paths and any parent-directory (..) component, then resolves the profiling prefix relative to the model directory (config_->config_path). Applied at the enable_profiling sink.
  • test/model_tests.cpp: add EnableProfilingRejectsPathTraversal and EnableProfilingRejectsAbsolutePath.

The custom fs::path in this repo has no canonicalization primitives, so containment is enforced lexically (reject absolute + ..), which is sufficient to keep the resolved path within the model directory subtree.

Notes / scope

  • The host-controlled runtime option (SetRuntimeOption("enable_profiling", ...), model.cpp / State::SetRunOption) is intentionally left unchanged — that path is not attacker-controlled.
  • Behavior change: config-sourced relative profiling paths now resolve relative to the model directory (previously the process CWD). This matches how other config paths (e.g. custom_ops_library) resolve and is the contained/expected default.
  • As the reporter notes, the co-located custom_ops_library field is a higher-value target but reflects the documented model-package trust boundary; this PR scopes to the enable_profiling hardening.

Testing

New regression tests use the existing OgaConfig::Overlay pattern on tiny-random-gpt2-fp32. Local build isn't available in my environment (root-owned build dir / jiafa-dev container needed); relying on CI to build and run the C++ unit tests.

@jiafatom jiafatom requested a review from a team as a code owner July 6, 2026 18:10
Copilot AI review requested due to automatic review settings July 6, 2026 18:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens handling of the enable_profiling value sourced from genai_config.json to mitigate path traversal (CWE-22) by rejecting absolute paths / .. components and resolving the profiling prefix relative to the model’s config directory.

Changes:

  • Added ResolveContainedConfigPath() and applied it to the config-sourced enable_profiling sink before calling Ort::SessionOptions::EnableProfiling().
  • Added C++ regression tests ensuring traversing and absolute enable_profiling values are rejected during model load.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/models/model.cpp Adds lexical containment validation for config-sourced profiling prefix and resolves it relative to the model directory.
test/model_tests.cpp Adds regression tests for rejecting traversing and absolute profiling paths from config overlays.

Comment thread src/models/model.cpp
The 'enable_profiling' field from genai_config.json was passed verbatim to
OnnxRuntime's EnableProfiling(), so a malicious model package could use path
traversal (e.g. '../../../etc/cron.d/ort' or a Windows Startup path) to write
ORT profiling output to arbitrary filesystem locations when the session closes.

Add defense-in-depth path containment: reject absolute paths and any
parent-directory ('..') component, and resolve the profiling prefix relative to
the model directory. The separate host-controlled runtime option
(SetRuntimeOption 'enable_profiling') is unaffected.

Add regression tests covering traversal and absolute enable_profiling paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jiafatom jiafatom force-pushed the fix/enable-profiling-path-containment branch from e701924 to aed4486 Compare July 7, 2026 23:06
…t/space

Windows trims trailing dots and spaces from path components, so values like
'.. ' or '...' could be interpreted as a parent/current directory and bypass
the lexical '..' check. Reject any component that collapses to only dots/spaces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/models/model.cpp

// Validates that a path-like string from the (untrusted) model config stays
// within the model directory, then resolves it relative to that directory.
// Rejects absolute paths and any current/parent-directory (".", "..") component

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same question as here in the other PR

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.

3 participants