sai: give rs274 its own tool mmap instead of truncating $HOME/.tool.mmap - #4385
Conversation
3c37f31 to
74522c6
Compare
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.
74522c6 to
8c8e048
Compare
| // close, returns at the tool_mmap_base guard above. | ||
| tool_mmap_base = (char*)0; |
There was a problem hiding this comment.
Please use NULL or nullptr instead of manual casts of the value zero (0).
There was a problem hiding this comment.
Done with nullptr at 241 and also at 200 (another one).
|
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.
Yes. Its in master. I wasn't sure if you want to retarget this, or do a new PR. Just LMK, or DIY. |
|
Yes, then you need to retarget this PR to master. |
|
The bug is identical in master, so the fix is wanted in both. The question is just direction. I asked tzuohann to point this at 2.9 since it was reported there (2.9.8, still in 2.9.10). Our usual flow is master-first, then backport. Would the reverse work here: keep this PR on 2.9 and merge 2.9 into master to carry it up? I checked the patch against both branches; it applies cleanly to 2.9, and the master differences in these files are only cosmetic, so the merge-up should be near-trivial. If you would rather have master-first, no problem, tzuohann can retarget and we backport. |
|
No, by all means, go for it. If this needs to be applied in both places, then it does not really matter in which order. |
Running
rs274while a LinuxCNC session is up replaces that session's tooltable 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.mmapO_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,--helpincluded, and-t(read at :583) cannot prevent it.io, milltask, halui and the Python bindings all map that same inode
MAP_SHARED.O_TRUNCkeeps the inode, so nothing re-maps and nothingerrors — the running session simply starts using the sample table.
Reproduce:
Patch:
Seen on 2.9.8, identical at 2.9.10.