Skip to content

Release 0.20.0 - #2

Merged
ersinkoc merged 28 commits into
mainfrom
polish-0.20
Oct 22, 2026
Merged

ersinkoc merged 28 commits into
mainfrom
polish-0.20

Conversation

@ersinkoc

Copy link
Copy Markdown
Owner

Release 0.20.0: three audit rounds of fixes. See CHANGELOG.md, section 0.20.0.

Highlights:

  • Security: install only where administrators alone control the program; the install folder is secured through handles; the updater re-checks the MSI in an admin-only folder; the hardware provider no longer self-extracts; private Telegram settings from creation; a stricter pipe DACL; the MCP and CLI trust only the real service and require create_time for control.
  • Policies never end or suspend service hosts or RustWTM; limits and settings changes always let go; game mode's power plan survives a crash.
  • GUI: confirmations for service and firewall changes; no actions on unlisted or reused PIDs.
  • ETW sessions no longer pile up; GPU type load is its busiest engine.

Not run locally (the shell was not elevated), so they are covered here by CI: the elevated install.ps1/uninstall.ps1 paths, the MSI with the two provider DLLs, and the SCM tests with RUSTWTM_ALLOW_ANY_INSTALL_DIR.

An audit proof that reserved nearly the whole address space as thread
stacks crashed Windows three times (bugcheck 0x1A / 0x3453 at process
teardown). A PreToolUse hook now refuses such code before it is written or
run, and CLAUDE.md / AGENTS.md require failure paths to be proven through
a seam in the code instead.
Fixes: client slots leaked when a client thread could not start; a frame
header alone reserved up to 32 MiB; unblocking deleted another program's
IFEO debugger; a minute written twice lost its first part; an update check
replaced an install under way; stale original priority on rollback; kill
tree on a reused PID; exit code 259 read as running; long window titles
matched a prefix; MCP: unknown fresh PIDs, non-UTF-8 lines, batches and
malformed pids.

Snapshots and recent points carry their measurement time. The data age,
the stale warning and the graph time axes use it, and a snapshot received
again adds no graph point. The frame benchmark covers 1k/10k processes with
and without new data each frame.
Processes, Services, Network and Ports use theme::search_bar (Ctrl+F,
Escape and a clear button); table toolbars wrap in narrow windows; Network
and Ports show an empty state with a way to clear the search.
An adapter coming back up added its whole count since boot to one sample,
and one going away hid the others' traffic. Clients no longer trust the
service pipe while the service is installed but not running.
A caller's process name with no creation time could take a protected
process (or lsass for a dump) past the name checks; the image actually
opened is now checked too. A hardware provider that failed once was never
started again, leaving fan control dead until the service restarted.
…cons

A failed accept closed the pending pipe instance, leaving the name free
for a second. Wait-chain requests behind a walk stuck in a frozen process
each left a thread blocked forever. Small icons on scaled displays were
read as a 16 px corner.
A relative CPU cap in a rule without a CPU threshold resolved against 0%
and all but froze the process; such rules are refused and the cap never
resolves below the threshold. Removing or editing a rule left what it
applied in place. After switching from dry-run to enforcing, policies
treated their previews as done and never acted on running processes.
End-to-end engine and GUI backend tests no longer skip on machines with
the service installed, since test pipes are not verified against it.
Blocking consent.exe and the like left no way back to an elevated RustWTM.
The service now takes ownership of its data folder and resets the
permissions of everything in it, so a folder or files a standard user
created before install no longer let them steer the service.
…rnor

Replaces the recursive permission reset, which a junction planted in a
pre-created folder could have pointed at system files. The service now
opens its data folder without following links; a link, or a folder owned
by anyone but SYSTEM or Administrators, is moved aside and created anew,
and a trusted folder gets its DACL through the handle only.

History divides an app's CPU and GPU by the samples of the whole minute, so
an app seen briefly no longer reads as a chronic hog. Fully throttled
processes no longer hold the governor's offender slots.
The folder is kept only when it and everything in it, looked at without
following links, belong to SYSTEM or Administrators, are no links or extra
hard links, and let nobody else write; otherwise it is set aside whole
under an unguessable name. When it cannot be made RustWTM's, the service
stops with a service-specific error instead of running on it.

Lifting a CPU and memory limit tries both even if the first fails, and
keeps the job of a limit that could not be lifted so it can be lifted
later; app budgets are retried every tick.
Panic, End all and Calm them all opened a connection per process; past
the service's 16 clients the rest were dropped and those processes kept
running, while the message showed only the last success. Requests now go
one after another over one connection per burst and are reported
together, failures first. Acting on a process that is not shown no longer
replaces the details of the one that is.
create_time is past what JavaScript clients keep exact in a JSON number:
it is offered as text and a rounded number still matches. kill --tree no
longer ends (and fails on) a match inside another match's tree, and
free-port ignores a process that got a port owner's PID after the lookup.
After the service connection was lost the first local sample averaged
CPU, disk and network over the whole time the service had been used.
Limits set while the window ran without the service could not be lifted
once it connected. The kill list saved over whichever entry moved into
the edited one's place; the kill and limit forms emptied before the save
was confirmed; Settings saved over changes made elsewhere meanwhile
without a word.
Interactive users had GENERIC_WRITE on the service pipe, which includes
FILE_CREATE_PIPE_INSTANCE: any of them could add a server instance of
the name next to the service. They now get exactly what a client opens
with (FILE_GENERIC_READ | FILE_WRITE_DATA); the server's own account
keeps full rights as the pipe's owner.
Updates waiting from before the bot started (hours old, or already
handled before a restart) were run again, a reused PID included; they are
now dropped first. Commands addressed to another bot ran here. One chat
that cannot be reached stopped alerts to the others and the reading of
commands. /suspend froze up to 50 processes unasked, svchost included,
which takes the network and the way back; suspending several is asked
first and svchost refused. Swarm buttons with long names broke /swarms;
long replies were never sent; removed chats kept their waits.
… token

telegram.toml.new was created with the data folder's inherited
permissions (Users may read) and restricted only after the token was
written; a handle opened meanwhile kept read access. It is now created
with its private descriptor. A parse error quoted the failing line, which
could be the token, into the event log every user can read.
…cripts, updater and release

- wtm-service install refuses a folder others can change (override for development)
- install.ps1/uninstall.ps1 use admin-only folders and never follow links
- updater re-checks the MSI after copying it to an admin-only folder
- hardware provider no longer self-extracts native libraries
- game mode's replaced power plan survives a crash
- release workflow: read-only build, pinned actions, separate publish job
The script checked for a link and then changed the folder by name, leaving
a moment to swap it; wtm-service prepare-install-dir now does it through
handles and refuses folders others could move. The install-location check
refuses links among the folders above, so it checks the folders the path
really goes through.
- engines of one type are no longer summed; a process shows its busiest engine
- session names carry the owner's start time, so a reused PID frees an orphan
- the window waits for its worker on close and stops an unread disk trace
- the service clears sessions closed windows left, every 10 minutes
- Ports tab: programs missing from the process list are not ended
- local mode: calming spares the window in front (by its PID)
- service Stop/Restart/Disable and blocking network access are confirmed
- Network tab: the selection filter needs the same process; Show all
- Apps view sums network rates
- Disks tab checks a typed folder off the UI thread
…service

- MCP control tools require create_time; unknown tools and malformed
  messages get the JSON-RPC errors; incomplete wait-chain analyses say so
- wtm panic and recent_events refuse an unverified pipe server
- kill-tree counts children already gone as ended; free-port lists all failures
…nswers

- why_alive and process_modules take the start time and hold a handle
- HTTP answers limited in bytes and total time (service memory, hangs)
- command_line retries when the command line changes between calls
- restart_service waits when the status cannot be read
…let go

- automatic kill/suspend refused for svchost.exe and RustWTM's programs;
  config rejects such kill entries and rules, and '*' with kill/suspend
- insights skip session 0
- leftover limits from a failed lift are retried; lifted at service stop
- app-limit budgets of vanished groups are lifted, not dropped
- governor, memory guard, insights and game mode restart on settings change
- a failed history query keeps the findings
- swarm ages are immune to the clock being set forward
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 60f378ac-8ca6-4cf2-8483-85eb58917d51
📥 Commits

Reviewing files that changed from the base of the PR and between 81f36b6 and a3da743.

⛔ Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing: 76
  • .claude/hooks/guard_exhaustion.py
  • .claude/settings.json
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .gitignore
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • Cargo.toml
  • PLAN.md
  • README.md
  • crates/wtm-cli/src/actions.rs
  • crates/wtm-cli/src/main.rs
  • crates/wtm-cli/src/mcp.rs
  • crates/wtm-core/src/control.rs
  • crates/wtm-core/src/hardware.rs
  • crates/wtm-core/src/lib.rs
  • crates/wtm-core/src/model.rs
  • crates/wtm-core/src/protection.rs
  • crates/wtm-core/src/sampler.rs
  • crates/wtm-core/src/update.rs
  • crates/wtm-core/tests/control.rs
  • crates/wtm-gui/locales/messages.json
  • crates/wtm-gui/src/alert.rs
  • crates/wtm-gui/src/app.rs
  • crates/wtm-gui/src/backend.rs
  • crates/wtm-gui/src/disks_tab.rs
  • crates/wtm-gui/src/kill_tab.rs
  • crates/wtm-gui/src/limits_tab.rs
  • crates/wtm-gui/src/overview.rs
  • crates/wtm-gui/src/ports_tab.rs
  • crates/wtm-gui/src/processes.rs
  • crates/wtm-gui/src/settings.rs
  • crates/wtm-gui/src/theme.rs
  • crates/wtm-gui/src/ui_tests.rs
  • crates/wtm-gui/src/views.rs
  • crates/wtm-ipc/src/lib.rs
  • crates/wtm-policy/src/bench.rs
  • crates/wtm-policy/src/config.rs
  • crates/wtm-policy/src/governor.rs
  • crates/wtm-policy/src/insights.rs
  • crates/wtm-policy/src/kill.rs
  • crates/wtm-policy/src/memory_guard.rs
  • crates/wtm-policy/src/rules.rs
  • crates/wtm-policy/src/telegram.rs
  • crates/wtm-service/src/app_limits.rs
  • crates/wtm-service/src/blocker.rs
  • crates/wtm-service/src/engine.rs
  • crates/wtm-service/src/executor.rs
  • crates/wtm-service/src/history.rs
  • crates/wtm-service/src/ipc_server.rs
  • crates/wtm-service/src/main.rs
  • crates/wtm-service/src/service.rs
  • crates/wtm-service/src/telegram.rs
  • crates/wtm-sys/src/control.rs
  • crates/wtm-sys/src/ending.rs
  • crates/wtm-sys/src/error.rs
  • crates/wtm-sys/src/etw.rs
  • crates/wtm-sys/src/gpu.rs
  • crates/wtm-sys/src/http.rs
  • crates/wtm-sys/src/icon.rs
  • crates/wtm-sys/src/modules.rs
  • crates/wtm-sys/src/pipe.rs
  • crates/wtm-sys/src/security.rs
  • crates/wtm-sys/src/services.rs
  • crates/wtm-sys/src/system.rs
  • crates/wtm-sys/src/waits.rs
  • crates/wtm-sys/src/window.rs
  • crates/wtm-sys/tests/tick_cost.rs
  • docs/0.13.0-review.md
  • docs/configuration.md
  • hardware/Wtm.Hardware/Wtm.Hardware.csproj
  • installer/RustWTM.wxs
  • scripts/ci/verify-install.ps1
  • scripts/install.ps1
  • scripts/uninstall.ps1
+----------------------------------------------------------------------+
| I am here to kick bugs and chew carrots. And I'm all out of carrots. |
+----------------------------------------------------------------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…hape

Only debuggers ending in --blocked counted as RustWTM's, so a blocker with
another command (as the elevated CI test uses) left its entry behind.
@ersinkoc
ersinkoc marked this pull request as ready for review October 22, 2026 11:56
@ersinkoc
ersinkoc merged commit 091cef9 into main Oct 22, 2026
5 checks passed
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