Skip to content

fix(profiler): quote paths in pprof command builders to prevent shell injection - #2243

Closed
noron12234 wants to merge 1 commit into
TEN-framework:mainfrom
noron12234:fix/pprof-cmd-shell-injection
Closed

fix(profiler): quote paths in pprof command builders to prevent shell injection#2243
noron12234 wants to merge 1 commit into
TEN-framework:mainfrom
noron12234:fix/pprof-cmd-shell-injection

Conversation

@noron12234

Copy link
Copy Markdown
Contributor

Problem

tools/profiler/gperftools/utils/pprof_cmd.py and tools/profiler/pprof/utils/pprof_cmd.py build shell command strings by concatenating caller-supplied file paths directly into the string. The resulting string is then executed by four dumper / comparer scripts via subprocess.run(cmd, shell=True):

  • tools/profiler/gperftools/dump_heap_files_to_raw.py:38
  • tools/profiler/gperftools/dump_raw_files_to_text.py:37
  • tools/profiler/gperftools/compare_heaps.py:25
  • tools/profiler/pprof/dump_heap_files_to_text.py:37

If any of bin, heapFile, rawFile, textFile, outputFile, port, or outputType contains shell metacharacters, the shell interprets them rather than passing them to google-pprof / go tool pprof. All six builders have this issue.

This mirrors the concern addressed in #2240 (shell=True in run_script.py), one layer up.

Verification

Drove the real pprof_cmd modules against a true-substituted binary so the shell parses+evaluates argv without needing google-pprof / go installed. Payload: '; touch /tmp/PPROF_PWNED_MARKER; # passed as heapFile.

builder before after
gperf.convert_heap_to_text_cmd injection ran blocked
gperf.convert_heap_to_raw_cmd injection ran blocked
gperf.convert_raw_to_text_cmd injection ran blocked
gperf.compare_heaps_cmd injection ran blocked
go.convert_heap_to_text_cmd injection ran blocked
go.show_heap_in_browser_cmd injection ran blocked

Benign paths produce byte-for-byte identical commands (shlex.quote is a no-op on shell-safe strings), so no caller behavior changes:

convert_heap_to_raw_cmd('./mybin', 'heap.out', 'o.raw')
→ google-pprof --raw ./mybin heap.out > o.raw

Why this shape

Two possible fixes:

  1. shlex.quote at the string-building layer (this PR) — preserves str return type, keeps the > redirection in the string, no changes to the four downstream callers.
  2. Return list[str] argv + move redirection into Python — deeper hardening (removes shell=True entirely, matching fix: avoid shell=True in run_script.py build helpers #2240), but touches four caller files and changes their " & ".join(...) parallel-execution pattern.

I picked (1) because the parallel-execution pattern (" & ".join(cmds) in the dumpers) has its own semantics worth preserving in a separate change — reviewer can decide independently whether to accept (2) as a follow-up. Happy to extend this PR to option (2) if preferred.

Files changed

  • tools/profiler/gperftools/utils/pprof_cmd.py (4 builders quoted)
  • tools/profiler/pprof/utils/pprof_cmd.py (2 builders quoted, port cast + quoted)

… injection

The pprof_cmd builders in tools/profiler/{gperftools,pprof}/utils/pprof_cmd.py
concatenated caller-supplied file paths straight into shell command strings
that are later run via subprocess.run(cmd, shell=True) in the four dumper /
comparer scripts. A path containing shell metacharacters (e.g. `; touch X; #`)
was interpreted by the shell rather than passed to google-pprof / go tool pprof.

Fix: wrap every path with shlex.quote() when building the command string.
Preserves the existing str return type and the '>' redirection expected by all
downstream callers — no caller changes needed.

Verified:
  payload                              BEFORE           AFTER
  '; touch /tmp/MARKER; #'             injection ran    injection blocked

Benign paths produce byte-for-byte identical commands (shlex.quote is a no-op
on shell-safe strings).
@noron12234
noron12234 requested a review from halajohn as a code owner July 27, 2026 07:37
@noron12234

Copy link
Copy Markdown
Contributor Author

Closing this for now — I'm stepping back to re-review my own work before asking for anyone's time. Thanks, and sorry for the noise.

@noron12234 noron12234 closed this Jul 29, 2026
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.

1 participant