Sunday, August 23, 2026

[spwhitton@spwhitton.name: [oss-security] Emacs zero-click local command execution via TRAMP]

Hi, This could be included in the emacs port for people using tramp (editing via ssh) on lower trust servers... I've replaced the diff by a patch for the port (the orginial pathe didn't apply cleanly, fixed too). Slightly tested with my own tramp uses (did not try to reproduce the issue). Also from @anticomputer@infosec.exchange on Mastodon: for the #emacs folks re: https://www.openwall.com/lists/oss-security/2026/08/21/1 ... to be clear: the implication here is that if you e.g. navigate a malicious git repo with dired (just opening the dir is enough) these bugs result in arbitrary command execution, because dired will stat the files by default ... so definitely pick up these fixes if you can 👍️ ----- Forwarded message from Sean Whitton <spwhitton@spwhitton.name> ----- Date: Fri, 21 Aug 2026 14:33:59 +0100 From: Sean Whitton <spwhitton@spwhitton.name> To: oss-security@lists.openwall.com Cc: Bas Alberts <anticomputer@github.com>, Eli Zaretskii <eliz@gnu.org>, Michael Albinus <michael.albinus@gmx.de> Subject: [oss-security] Emacs zero-click local command execution via TRAMP Bas Alberts of the GitHub Security Lab discovered a local command execution vulnerability in GNU Emacs 30.2 onwards, and possibly earlier. I am attaching a fix prepared by Michael Albinus, the TRAMP maintainer. Here is Bas's description of the problem (edited down a bit). --8<---------------cut here---------------start------------->8--- 1. Tramp: local shell command injection through the user field (tramp-sh.el, CRITICAL) tramp-maybe-open-connection builds the login command by expanding tramp-login-args (%u, %h, ...) and joining the words with string-join, unquoted, into a command line that is sent to a live LOCAL shell (tramp-encoding-shell, i.e. /bin/sh) that Tramp has already spawned for the connection: - tramp-sh.el:5489-5514 unquoted string-join of login args - tramp-sh.el:5406-5413 the local shell the string is sent to tramp-user-regexp (tramp.el:1073) is (+ (not (any "/:|[]" blank))), which admits $ ( ) ; ` ' " \ and more. tramp-dissect-file-name performs no sanitization. Consequence: merely stat-ing a file name such as /ssh:$(cd;touch$IFS'pwned')@127.0.0.1:/x e.g. via file-exists-p, executes the $(...) payload in the local shell during connection setup, before and regardless of any actual ssh connection or server. The excluded characters are easily worked around within the allowed charset: $IFS substitutes for blanks, $(printf$IFS'\057') composes the excluded /, and $(cd;...) sidesteps absolute paths entirely, so arbitrary commands are expressible. 2. Tramp: file name dispatch regexp is line-anchored, not string-anchored (tramp.el, CRITICAL enabler) tramp-build-prefix-regexp (tramp.el:1025-1027) anchors with rx "bol"; the docstring even says the result "Should always start with ^". The derived tramp-file-name-regexp is what Tramp registers in file-name-handler-alist once loaded (replacing the string-anchored tramp-initial-file-name-regexp autoload entry, tramp.el:1284-1288). Consequence: after Tramp is loaded, any file name merely CONTAINING newline + "/ssh:..." is dispatched to Tramp handlers, and tramp-dissect-file-name parses the embedded line, feeding defect 1. File names cannot contain "/", but two on-disk carriers exist: a. symlink targets (arbitrary bytes except NUL, committable to git); b. a directory whose name ends in a newline, containing a file named "ssh:...": the path component boundary supplies the "/", so any code that composes the path (e.g. directory-files-recursively) produces a string containing "\n/ssh:...". Verified: recursively scanning such a tree executes the payload with Tramp loaded. Additionally, an ABSOLUTE symlink target "/ssh:$(...)@host:/x" matches even the string-anchored autoload regexp, so no Tramp preload is required for carrier (a). --8<---------------cut here---------------end--------------->8--- -- Sean Whitton Author: Michael Albinus <michael.albinus@gmx.de> Index: Makefile =================================================================== RCS file: /cvs/ports/editors/emacs/Makefile,v diff -u -p -u -r1.130 Makefile --- Makefile 24 Jun 2026 10:55:27 -0000 1.130 +++ Makefile 23 Aug 2026 17:28:31 -0000 @@ -2,7 +2,7 @@ COMMENT= GNU editor: extensible, custom VERSION= 30.2 DISTNAME= emacs-${VERSION} -REVISION= 3 +REVISION= 4 CATEGORIES= editors Index: patches/patch-lisp_net_tramp-gvfs_el =================================================================== RCS file: patches/patch-lisp_net_tramp-gvfs_el diff -N patches/patch-lisp_net_tramp-gvfs_el --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lisp_net_tramp-gvfs_el 23 Aug 2026 17:28:31 -0000 @@ -0,0 +1,134 @@ +Fix shell injections in tramp + +https://www.openwall.com/lists/oss-security/2026/08/21/1 + +Index: lisp/net/tramp-gvfs.el +--- lisp/net/tramp-gvfs.el.orig ++++ lisp/net/tramp-gvfs.el +@@ -1241,7 +1241,7 @@ file names." + (when (string-match + (rx bos "/" (+ (not "/")) (group "/.." (? "/"))) localname) + (setq localname (replace-match "/" t t localname 1))) +- (when (string-match (rx bol "/.." (? "/")) localname) ++ (when (string-match (rx bos "/.." (? "/")) localname) + (setq localname (replace-match "/" t t localname)))) + ;; There might be a double slash. Remove this. + (while (string-match "//" localname) +@@ -1335,8 +1335,8 @@ If FILE-SYSTEM is non-nil, return file system attribut + (with-parsed-tramp-file-name filename nil + (setq localname (file-name-unquote localname)) + (if (or (and (string-match-p +- (rx bol (| "afp" (: "dav" (? "s")) "smb") eol) method) +- (string-match-p (rx bol (? "/") (+ (not "/")) eol) localname)) ++ (rx bos (| "afp" (: "dav" (? "s")) "smb") eos) method) ++ (string-match-p (rx bos (? "/") (+ (not "/")) eos) localname)) + (string-equal localname "/")) + (tramp-gvfs-get-root-attributes filename) + (assoc +@@ -1370,7 +1370,11 @@ If FILE-SYSTEM is non-nil, return file system attribut + (lambda (x) + (unibyte-string (string-to-number (match-string 1 x) 16))) + res-symlink-target) +- 'utf-8))) ++ 'utf-8)) ++ ;; If the resulting localname looks remote, we must quote it ++ ;; for security reasons. ++ (when (tramp-tramp-file-p res-symlink-target) ++ (setq res-symlink-target (file-name-quote res-symlink-target 'top)))) + ;; ... number links + (setq res-numlinks + (string-to-number +@@ -1773,14 +1777,14 @@ ID-FORMAT valid values are `string' and `integer'." + "Retrieve file name from D-Bus OBJECT-PATH." + (dbus-unescape-from-identifier + (replace-regexp-in-string +- (rx bol (* nonl) "/" (group (+ (not "/"))) eol) "\\1" object-path))) ++ (rx bos (* nonl) "/" (group (+ (not "/"))) eos) "\\1" object-path))) + + (defun tramp-gvfs-url-host (url) + "Return the host name part of URL, a string. + We cannot use `url-host', because `url-generic-parse-url' returns + a downcased host name only." + (and (stringp url) +- (string-match (rx bol (+ alnum) "://" (group (+ (not (any "/:"))))) url) ++ (string-match (rx bos (+ alnum) "://" (group (+ (not (any "/:"))))) url) + (match-string 1 url))) + + ;; This is used in GNU ELPA package tramp-locproc.el. +@@ -1934,7 +1938,7 @@ Their full names are \"org.gtk.vfs.MountTracker.mounte + (cadr (assoc "ssl" (cadr mount-spec))))) + (uri (tramp-gvfs-dbus-byte-array-to-string + (cadr (assoc "uri" (cadr mount-spec)))))) +- (when (string-match (rx bol (group (| "afp" "smb"))) method) ++ (when (string-match (rx bos (group (| "afp" "smb"))) method) + (setq method (match-string 1 method))) + (when (and (string-equal "dav" method) (string-equal "true" ssl)) + (setq method "davs")) +@@ -2034,7 +2038,7 @@ Their full names are \"org.gtk.vfs.MountTracker.mounte + (or + (cadr (assoc "share" (cadr mount-spec))) + (cadr (assoc "volume" (cadr mount-spec))))))) +- (when (string-match (rx bol (group (| "afp" "smb"))) method) ++ (when (string-match (rx bos (group (| "afp" "smb"))) method) + (setq method (match-string 1 method))) + (when (and (string-equal "dav" method) (string-equal "true" ssl)) + (setq method "davs")) +@@ -2067,7 +2071,7 @@ Their full names are \"org.gtk.vfs.MountTracker.mounte + (string-equal host (tramp-file-name-host vec)) + (string-equal port (tramp-file-name-port vec)) + (string-match-p +- (rx bol "/" (literal (or share ""))) ++ (rx bos "/" (literal (or share ""))) + (tramp-file-name-unquote-localname vec))) + ;; Set mountpoint and location. + (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint) +@@ -2093,7 +2097,7 @@ Their full names are \"org.gtk.vfs.MountTracker.mounte + (defun tramp-gvfs-mount-spec-entry (key value) + "Construct a mount-spec entry to be used in a mount_spec. + It was \"a(say)\", but has changed to \"a{sv})\"." +- (if (string-match-p (rx bol "(aya{sv})") tramp-gvfs-mountlocation-signature) ++ (if (string-match-p (rx bos "(aya{sv})") tramp-gvfs-mountlocation-signature) + (list :dict-entry key + (list :variant (tramp-gvfs-dbus-string-to-byte-array value))) + (list :struct key (tramp-gvfs-dbus-string-to-byte-array value)))) +@@ -2112,9 +2116,9 @@ It was \"a(say)\", but has changed to \"a{sv})\"." + (tramp-media-device-port media) (tramp-file-name-port vec))) + (localname (tramp-file-name-unquote-localname vec)) + (share (when (string-match +- (rx bol (? "/") (group (+ (not "/")))) localname) ++ (rx bos (? "/") (group (+ (not "/")))) localname) + (match-string 1 localname))) +- (ssl (if (string-match-p (rx bol (| "davs" "nextcloud")) method) ++ (ssl (if (string-match-p (rx bos (| "davs" "nextcloud")) method) + "true" "false")) + (mount-spec + `(:array +@@ -2123,7 +2127,7 @@ It was \"a(say)\", but has changed to \"a{sv})\"." + (list (tramp-gvfs-mount-spec-entry "type" "smb-share") + (tramp-gvfs-mount-spec-entry "server" host) + (tramp-gvfs-mount-spec-entry "share" share))) +- ((string-match-p (rx bol (| "davs" "nextcloud")) method) ++ ((string-match-p (rx bos (| "davs" "nextcloud")) method) + (list (tramp-gvfs-mount-spec-entry "type" "dav") + (tramp-gvfs-mount-spec-entry "host" host) + (tramp-gvfs-mount-spec-entry "ssl" ssl))) +@@ -2137,7 +2141,7 @@ It was \"a(say)\", but has changed to \"a{sv})\"." + ((string-equal "nextcloud" method) + (list (tramp-gvfs-mount-spec-entry "type" "owncloud") + (tramp-gvfs-mount-spec-entry "host" host))) +- ((string-match-p (rx bol "http") method) ++ ((string-match-p (rx bos "http") method) + (list (tramp-gvfs-mount-spec-entry "type" "http") + (tramp-gvfs-mount-spec-entry + "uri" +@@ -2154,8 +2158,8 @@ It was \"a(say)\", but has changed to \"a{sv})\"." + ,@(when port + (list (tramp-gvfs-mount-spec-entry "port" port))))) + (mount-pref +- (if (and (string-match-p (rx bol "dav") method) +- (string-match (rx bol (? "/") (+ (not "/"))) localname)) ++ (if (and (string-match-p (rx bos "dav") method) ++ (string-match (rx bos (? "/") (+ (not "/"))) localname)) + (match-string 0 localname) + (tramp-gvfs-get-remote-prefix vec)))) + Index: patches/patch-lisp_net_tramp-rclone_el =================================================================== RCS file: patches/patch-lisp_net_tramp-rclone_el diff -N patches/patch-lisp_net_tramp-rclone_el --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lisp_net_tramp-rclone_el 23 Aug 2026 17:28:31 -0000 @@ -0,0 +1,25 @@ +Fix shell injections in tramp + +https://www.openwall.com/lists/oss-security/2026/08/21/1 + +Index: lisp/net/tramp-rclone.el +--- lisp/net/tramp-rclone.el.orig ++++ lisp/net/tramp-rclone.el +@@ -191,7 +191,7 @@ arguments to pass to the OPERATION." + (with-tramp-connection-property nil "rclone-device-names" + (tramp-compat-seq-keep + (lambda (line) +- (when (string-match (rx bol (group (+ (not blank))) ":" eol) line) ++ (when (string-match (rx bos (group (+ (not blank))) ":" eos) line) + `(nil ,(match-string 1 line)))) + (tramp-process-lines nil tramp-rclone-program "listremotes")))) + +@@ -365,7 +365,7 @@ file names." + (tramp-rclone-maybe-open-connection v) + ;; TODO: This shall be handled by `expand-file-name'. + (setq localname +- (replace-regexp-in-string (rx bol ".") "" (or localname ""))) ++ (replace-regexp-in-string (rx bos ".") "" (or localname ""))) + (format "%s%s" (tramp-fuse-mounted-p v) localname))) + ;; It is a local file name. + filename)) Index: patches/patch-lisp_net_tramp-smb_el =================================================================== RCS file: patches/patch-lisp_net_tramp-smb_el diff -N patches/patch-lisp_net_tramp-smb_el --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lisp_net_tramp-smb_el 23 Aug 2026 17:28:31 -0000 @@ -0,0 +1,34 @@ +Fix shell injections in tramp + +https://www.openwall.com/lists/oss-security/2026/08/21/1 + +Index: lisp/net/tramp-smb.el +--- lisp/net/tramp-smb.el.orig ++++ lisp/net/tramp-smb.el +@@ -1550,7 +1550,7 @@ VEC or USER, or if there is no home directory, return + "Return the share name of LOCALNAME." + (save-match-data + (let ((localname (tramp-file-name-unquote-localname vec))) +- (when (string-match (rx bol (? "/") (group (+ (not "/"))) "/") localname) ++ (when (string-match (rx bos (? "/") (group (+ (not "/"))) "/") localname) + (match-string 1 localname))))) + + (defun tramp-smb-get-localname (vec) +@@ -1561,7 +1561,7 @@ If VEC has no cifs capabilities, exchange \"/\" by \"\ + (setq + localname + (if (string-match +- (rx bol (? "/") (+ (not "/")) (group "/" (* nonl))) localname) ++ (rx bos (? "/") (+ (not "/")) (group "/" (* nonl))) localname) + ;; There is a share, separated by "/". + (if (not (tramp-smb-get-cifs-capabilities vec)) + (mapconcat +@@ -1569,7 +1569,7 @@ If VEC has no cifs capabilities, exchange \"/\" by \"\ + (match-string 1 localname) "") + (match-string 1 localname)) + ;; There is just a share. +- (if (string-match (rx bol (? "/") (group (+ (not "/"))) eol) localname) ++ (if (string-match (rx bos (? "/") (group (+ (not "/"))) eol) localname) + (match-string 1 localname) + ""))) + Index: patches/patch-lisp_net_tramp_el =================================================================== RCS file: patches/patch-lisp_net_tramp_el diff -N patches/patch-lisp_net_tramp_el --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-lisp_net_tramp_el 23 Aug 2026 17:28:31 -0000 @@ -0,0 +1,65 @@ +Fix shell injections in tramp + +https://www.openwall.com/lists/oss-security/2026/08/21/1 + +Index: lisp/net/tramp.el +--- lisp/net/tramp.el.orig ++++ lisp/net/tramp.el +@@ -973,11 +973,11 @@ Used in `tramp-make-tramp-file-name'.") + + (defun tramp-build-prefix-regexp () + "Return `tramp-prefix-regexp'." +- (rx bol (literal (tramp-build-prefix-format)))) ++ (rx bos (literal (tramp-build-prefix-format)))) + + (defvar tramp-prefix-regexp nil ; Initialized when defining `tramp-syntax'! + "Regexp matching the very beginning of Tramp file names. +-Should always start with \"^\". Derived from `tramp-prefix-format'.") ++Should always start with \"\\\\=`\". Derived from `tramp-prefix-format'.") + + (defconst tramp-method-regexp-alist + `((default . ,(rx (| (literal tramp-default-method-marker) (>= 2 alnum)))) +@@ -1019,8 +1019,11 @@ Used in `tramp-make-tramp-file-name'.") + "Regexp matching delimiter between method and user or host names. + Derived from `tramp-postfix-method-format'.") + +-(defconst tramp-user-regexp (rx (+ (not (any "/:|" blank)))) +- "Regexp matching user names.") ++(defconst tramp-user-regexp ++ (rx (| (+ (not (any "/\\^$?*:;|[]{}()<>`'\"" blank))) ++ ;; Environment variable. ++ (: "$" (+ (any "_" alnum))))) ++ "Regexp matching user names.") + + (defconst tramp-prefix-domain-format "%" + "String matching delimiter between user and domain names.") +@@ -1801,6 +1804,8 @@ default values are used." + (hop (match-string (nth 5 tramp-file-name-structure) name)) + domain port v) + (when user ++ (while (string-match (rx bos "$" (group (+ (any "_" alnum))) eos) user) ++ (setq user (getenv (match-string 1 user)))) + (when (string-match tramp-user-with-domain-regexp user) + (setq domain (match-string 2 user) + user (match-string 1 user)))) +@@ -2491,9 +2496,9 @@ Fall back to normal file name handler if no Tramp file + ;; `file-remote-p' is called for everything, even for symbolic + ;; links which look remote. We don't want to get an error. + (non-essential (or non-essential (eq operation 'file-remote-p)))) ++ (setq filename (tramp-replace-environment-variables filename)) + (if (tramp-tramp-file-p filename) + (save-match-data +- (setq filename (tramp-replace-environment-variables filename)) + (with-parsed-tramp-file-name filename nil + (let ((current-connection tramp-current-connection) + (foreign +@@ -6537,6 +6542,9 @@ to cache the result. Return the modified ATTR." + (caar attr)) + (decode-coding-string + (match-string 1 (caar attr)) 'utf-8)))) ++ ;; Quote remote-like symlink. ++ (when (and (stringp (car attr)) (tramp-tramp-file-p (car attr))) ++ (setcar attr (file-name-quote (car attr) 'top))) + ;; Set file's gid change bit. + (setcar + (nthcdr 9 attr) -- Matthieu Herrb

Re: graphics/ffmpeg: update to 9.0.1

On Sun, Aug 23, 2026 at 06:50:10AM -0400, Brad Smith wrote: > On Sun, Aug 23, 2026 at 10:53:25AM +0200, Kirill A. Korinsky wrote: > > On Thu, 13 Aug 2026 12:13:54 +0200, > > Kirill A. Korinsky <kirill@korins.ky> wrote: > > > > > > ports@, Brad, > > > > > > here a diff to update graphics/ffmpeg to 9.0.1, and it fixes and bumps > > > x11/mplayer as well. > > > > > > Bulk on amd64 with previous diffs and this one successeded, unsigned > > > packages available here: https://kirill.korins.ky/pub/packages/ffmpeg-9.0.1/ > > > > > > Tests and OKs are welcome. > > > > > > > The previous version was without subject what is fixed now. > > > > Also, it survives a few bulks on amd64 but I haven't tested BTI/IBT amd64, > > so any tests are welcome. > > Here is an updated diff. The configure patch SSL changes can be > simplified a bit and add in my MIPS fix for CABAC. It works for me in some light testing. Tests run into segfaults on both amd64 and arm64, which is not necessarily a blocker but a bit annoying. Quite a few tests do run with amd64/ibt without SIGILL, so that is reassuring. apple M1: NEON: FAILURE: float_fft_16384_neon (segmentation fault) - av_tx [FAILED] checkasm: 1 of 8 tests failed threads=1 on both cpu0: 12th Gen Intel(R) Core(TM) i7-1260P, 1995.55 MHz, 06-9a-03, patch 0000043b cpu0: AMD Ryzen 7 PRO 5850U with Radeon Graphics, 1900.00 MHz, 19-50-00, patch 0a500012 I see this: ffmpeg version 9.0.1 Copyright (c) 2000-2026 the FFmpeg developers built with OpenBSD clang version 22.1.6 configuration: --enable-shared --arch=amd64 --cc=cc --cxx=c++ --enable-debug --disable-stripping --disable-indev=jack --disable-vulkan --enable-fontconfig --enable-frei0r --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libdav1d --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libharfbuzz --enable-libmp3lame --enable-libopus --enable-libspeex --enable-libsvtav1 --enable-libtheora --enable-libv4l2 --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-nonfree --enable-openssl --enable-libvidstab --extra-cflags='-I/usr/local/include -I/usr/X11R6/include' --extra-libs='-L/usr/local/lib -L/usr/X11R6/lib' --extra-ldsoflags= --mandir=/usr/local/man --objcc=/usr/bin/false --optflags='-O2 -pipe -g -Wno-redundant-decls' libavutil 61. 1.101 / 61. 1.101 libavcodec 63. 1.101 / 63. 1.101 libavformat 63. 1.101 / 63. 1.101 libavdevice 63. 1.101 / 63. 1.101 libavfilter 12. 1.101 / 12. 1.101 libswscale 10. 1.101 / 10. 1.101 libswresample 7. 1.101 / 7. 1.101 Stream mapping: crop:default -> Stream #0:0 (rawvideo) Segmentation fault (core dumped) threads=1 Program terminated with signal SIGSEGV, Segmentation fault. #0 memcpy (dst0=0xf3c943c2000, src0=0xf3c87d941c4 <ff_init_hscaler_mmxext+180>, length=44) at /usr/src/lib/libc/string/memcpy.c:97 97 TLOOP1(*dst++ = *src++); [Current thread is 1 (process 148644)] (gdb) bt #0 memcpy (dst0=0xf3c943c2000, src0=0xf3c87d941c4 <ff_init_hscaler_mmxext+180>, length=44) at /usr/src/lib/libc/string/memcpy.c:97 #1 0x00000f3c87d943b5 in ff_init_hscaler_mmxext (dstW=40032, xInc=65504, filterCode=0xf3c943c2000 "", filter=0xf3bda2a8000, filterPos=0xf3c966f6000, numSplits=<optimized out>) at src/libswscale/x86/hscale_fast_bilinear_simd.c:157 #2 0x00000f3c87d90eb0 in ff_sws_init_single_context (sws=0xf3c09903000, srcFilter=0xf3c227dd770, dstFilter=0xf3c227dd770) at src/libswscale/utils.c:1705 #3 0x00000f3c87c9c8ab in add_legacy_sws_pass (graph=0xf3c2131d400, src=0xf3c227dd988, dst=0xf3c227dd900, input=0x0, output=0xf3c227dd8e8) at src/libswscale/graph.c:584