Skip to content

sai: give rs274 its own tool mmap instead of truncating $HOME/.tool.mmap - #4385

Open
tzuohann wants to merge 2 commits into
LinuxCNC:2.9from
tzuohann:rs274-private-toolmmap
Open

sai: give rs274 its own tool mmap instead of truncating $HOME/.tool.mmap#4385
tzuohann wants to merge 2 commits into
LinuxCNC:2.9from
tzuohann:rs274-private-toolmmap

Conversation

@tzuohann

Copy link
Copy Markdown
Contributor

Running rs274 while a LinuxCNC session is up replaces that session's tool
table with the sample one (shipped with lcnc), until restart.

A lucky user will find that conflicting MDI or gcode stops running. A less
lucky user will find code running with offsets from sample tools - potentially
disastrous.

tool_mmap_creator() opens $HOME/.tool.mmap O_RDWR|O_CREAT|O_TRUNC
(tooldata_mmap.cc:33, :135) and sai calls it at driver.cc:570 — before
getopt() at :578, so it happens on every invocation, --help included, and
-t (read at :583) cannot prevent it.

io, milltask, halui and the Python bindings all map that same inode
MAP_SHARED. O_TRUNC keeps the inode, so nothing re-maps and nothing
errors — the running session simply starts using the sample table.

Reproduce:

rm -rf /tmp/rsx && mkdir -p /tmp/rsx
HOME=/tmp/rsx rs274 -g /dev/null
# /tmp/rsx/.tool.mmap, last_index=4, sample table

Patch:

Gives sai its own pid-qualified file under /tmp, unlinked on exit, so an
offline check can never touch a running session's table.
tool_mmap_close() already unlinks and tool_mmap_fname() already honours a
preset name; this only adds the setter to reach it. io and milltask are
unchanged.

Seen on 2.9.8, identical at 2.9.10.

Comment thread src/emc/sai/driver.cc
Comment thread src/emc/sai/driver.cc
@tzuohann
tzuohann force-pushed the rs274-private-toolmmap branch from 3c37f31 to 74522c6 Compare August 16, 2026 14:44
Comment thread src/emc/tooldata/tooldata_mmap.cc
tool_mmap_creator() opens the file O_RDWR|O_CREAT|O_TRUNC
(tooldata_mmap.cc:33, used at :135) and sai calls it at driver.cc:570 --
before getopt() at :578. Every rs274 invocation therefore empties
$HOME/.tool.mmap, including `rs274 --help` and including one that supplies
-t, since -t is not read until :583.

That file is not scratch space: tool_mmap_fname() builds it from
secure_getenv("HOME") with a fixed name, and io, milltask, halui and the
Python bindings all map that same inode MAP_SHARED. An offline parse run
beside a live session therefore replaces the running machine's tool table
with the compiled-in sample table. O_TRUNC preserves the inode, so nothing
re-maps and nothing is notified: the session simply observes its tools
change. Observed on a machine using [EMCIO]DB_PROGRAM -- 15 tools became the
4 sample entries mid-session, G43 applied 0.0000 for a tool that was no
longer in the table, and the tool-number/drawbar guard inhibited jog and
feed. DB_PROGRAM neither prevents nor repairs it: ioControl.cc creates the
mmap before the DB_ACTIVE branch, and io does not re-read afterwards.

Give sai its own file and unlink it on exit. tool_mmap_close() already
unlinks and tool_mmap_fname() already honours a preset filename -- this only
adds the setter to reach it. io and milltask are untouched and remain the
only creators of the shared file.

Two points from review, both addressed here:

mkstemp(), not a name built from the pid (grandixximo). TMPDIR is
world-writable and a pid is guessable, and the creator opens without O_EXCL
or O_NOFOLLOW, so a predictable name can be pre-created as a symlink and the
victim's rs274 then truncates the attacker's chosen file -- and an attacker
can blanket a pid range in advance. mkstemp() creates it atomically with
O_EXCL and mode 0600, and TOOL_MMAP_CREATOR_OPEN_FLAGS gains O_NOFOLLOW so
the creator refuses a symlink at that path even if one appears in the gap.

tool_mmap_close() is now safe as an atexit handler (BsAtHome). It called
exit(EXIT_FAILURE) when munmap failed, and calling exit() from within an
atexit handler is undefined behaviour; _exit would skip the remaining
handlers, so that is not the answer either. It now reports the failure,
closes the fd and returns.

Reproduce before the change:

    rm -rf /tmp/rsx && mkdir -p /tmp/rsx
    HOME=/tmp/rsx rs274 -g /dev/null
    # /tmp/rsx/.tool.mmap, last_index=4, holding
    # T1 z0.511 d0.125 / T2 z0.100 d0.0625 / T3 z1.273 d0.201 / T99999 P123

After: rs274 writes $TMPDIR/rs274.tool.mmap.XXXXXX, removes it on exit, and
does not open $HOME/.tool.mmap. Verified on a live machine: an rs274 run with
the real $HOME left last_index=15 and every tool untouched.
@tzuohann
tzuohann force-pushed the rs274-private-toolmmap branch from 74522c6 to 8c8e048 Compare August 17, 2026 02:31
Comment thread src/emc/tooldata/tooldata_mmap.cc
Comment thread src/emc/tooldata/tooldata_mmap.cc Outdated
Comment on lines +240 to +241
// close, returns at the tool_mmap_base guard above.
tool_mmap_base = (char*)0;

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.

Please use NULL or nullptr instead of manual casts of the value zero (0).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done with nullptr at 241 and also at 200 (another one).

Comment thread src/emc/tooldata/tooldata_mmap.cc
@BsAtHome

Copy link
Copy Markdown
Contributor

BTW, I see this is targeted at 2.9 branch. The same problem in master, I guess?

BsAtHome, review 2026-08-18: `(char*)0` is a hand-written null pointer. Both
occurrences in tooldata_mmap.cc now use nullptr. No behaviour change.
@tzuohann

Copy link
Copy Markdown
Contributor Author

BTW, I see this is targeted at 2.9 branch. The same problem in master, I guess?

Yes. Its in master. I wasn't sure if you want to retarget this, or do a new PR. Just LMK, or DIY.

@BsAtHome

Copy link
Copy Markdown
Contributor

Yes, then you need to retarget this PR to master.

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