diff options
| author | Philipp Stephani | 2020-12-14 21:25:11 +0100 |
|---|---|---|
| committer | Philipp Stephani | 2021-04-10 18:47:26 +0200 |
| commit | be8328acf9aa464f848e682e63e417a18529af9e (patch) | |
| tree | b7f8191920af5e326b2a2feac9ddbeb8551fadba | |
| parent | 53dfd85a7f971875e716a55f010ee508bce89eed (diff) | |
| download | emacs-scratch/seccomp-emacs-open.tar.gz emacs-scratch/seccomp-emacs-open.zip | |
Add support for --seccomp command-line option.scratch/seccomp-emacs-open
When passing this option on GNU/Linux, Emacs installs a Secure
Computing kernel system call filter. See Bug#45198.
* configure.ac: Check for seccomp header.
* src/emacs.c (usage_message): Document --seccomp option.
(emacs_seccomp): New wrapper for 'seccomp' syscall.
(load_seccomp, maybe_load_seccomp): New helper functions.
(main): Potentially load seccomp filters during startup.
(standard_args): Add --seccomp option.
* lisp/startup.el (command-line): Detect and ignore --seccomp option.
* test/src/emacs-tests.el (emacs-tests/seccomp/absent-file)
(emacs-tests/seccomp/empty-file)
(emacs-tests/seccomp/file-too-large)
(emacs-tests/seccomp/invalid-file-size): New unit tests.
(emacs-tests--with-temp-file): New helper macro.
* etc/NEWS: Document new --seccomp option.
| -rw-r--r-- | configure.ac | 5 | ||||
| -rw-r--r-- | etc/NEWS | 10 | ||||
| -rw-r--r-- | lisp/startup.el | 5 | ||||
| -rw-r--r-- | src/emacs.c | 167 | ||||
| -rw-r--r-- | test/src/emacs-tests.el | 131 |
5 files changed, 314 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac index 2c62a9fe6f7..684788a4d33 100644 --- a/configure.ac +++ b/configure.ac | |||
| @@ -4179,6 +4179,8 @@ fi | |||
| 4179 | AC_SUBST([BLESSMAIL_TARGET]) | 4179 | AC_SUBST([BLESSMAIL_TARGET]) |
| 4180 | AC_SUBST([LIBS_MAIL]) | 4180 | AC_SUBST([LIBS_MAIL]) |
| 4181 | 4181 | ||
| 4182 | AC_CHECK_HEADERS([linux/seccomp.h], [HAVE_SECCOMP=yes]) | ||
| 4183 | |||
| 4182 | OLD_LIBS=$LIBS | 4184 | OLD_LIBS=$LIBS |
| 4183 | LIBS="$LIB_PTHREAD $LIB_MATH $LIBS" | 4185 | LIBS="$LIB_PTHREAD $LIB_MATH $LIBS" |
| 4184 | AC_CHECK_FUNCS(accept4 fchdir gethostname \ | 4186 | AC_CHECK_FUNCS(accept4 fchdir gethostname \ |
| @@ -5672,7 +5674,8 @@ optsep= | |||
| 5672 | emacs_config_features= | 5674 | emacs_config_features= |
| 5673 | for opt in ACL CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \ | 5675 | for opt in ACL CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \ |
| 5674 | HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \ | 5676 | HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \ |
| 5675 | M17N_FLT MODULES NOTIFY NS OLDXMENU PDUMPER PNG RSVG SOUND THREADS TIFF \ | 5677 | M17N_FLT MODULES NOTIFY NS OLDXMENU PDUMPER PNG RSVG SECCOMP SOUND \ |
| 5678 | THREADS TIFF \ | ||
| 5676 | TOOLKIT_SCROLL_BARS UNEXEC X11 XAW3D XDBE XFT XIM XPM XWIDGETS X_TOOLKIT \ | 5679 | TOOLKIT_SCROLL_BARS UNEXEC X11 XAW3D XDBE XFT XIM XPM XWIDGETS X_TOOLKIT \ |
| 5677 | ZLIB; do | 5680 | ZLIB; do |
| 5678 | 5681 | ||
| @@ -90,6 +90,16 @@ lacks the terminfo database, you can instruct Emacs to support 24-bit | |||
| 90 | true color by setting 'COLORTERM=truecolor' in the environment. This is | 90 | true color by setting 'COLORTERM=truecolor' in the environment. This is |
| 91 | useful on systems such as FreeBSD which ships only with "etc/termcap". | 91 | useful on systems such as FreeBSD which ships only with "etc/termcap". |
| 92 | 92 | ||
| 93 | ** On GNU/Linux systems, Emacs now supports loading a Secure Computing | ||
| 94 | filter. To use this, you can pass a --seccomp=FILE command-line | ||
| 95 | option to Emacs. FILE must name a binary file containing an array of | ||
| 96 | 'struct sock_filter' structures. Emacs will then install that list of | ||
| 97 | Secure Computing filters into its own process early during the startup | ||
| 98 | process. You can use this functionality to put an Emacs process in a | ||
| 99 | sandbox to avoid security issues when executing untrusted code. See | ||
| 100 | the manual page for 'seccomp' for details about Secure Computing | ||
| 101 | filters. | ||
| 102 | |||
| 93 | 103 | ||
| 94 | * Changes in Emacs 28.1 | 104 | * Changes in Emacs 28.1 |
| 95 | 105 | ||
diff --git a/lisp/startup.el b/lisp/startup.el index b173d619733..4d4c65e6c41 100644 --- a/lisp/startup.el +++ b/lisp/startup.el | |||
| @@ -1097,7 +1097,7 @@ please check its value") | |||
| 1097 | ("--no-x-resources") ("--debug-init") | 1097 | ("--no-x-resources") ("--debug-init") |
| 1098 | ("--user") ("--iconic") ("--icon-type") ("--quick") | 1098 | ("--user") ("--iconic") ("--icon-type") ("--quick") |
| 1099 | ("--no-blinking-cursor") ("--basic-display") | 1099 | ("--no-blinking-cursor") ("--basic-display") |
| 1100 | ("--dump-file") ("--temacs"))) | 1100 | ("--dump-file") ("--temacs") ("--seccomp"))) |
| 1101 | (argi (pop args)) | 1101 | (argi (pop args)) |
| 1102 | (orig-argi argi) | 1102 | (orig-argi argi) |
| 1103 | argval) | 1103 | argval) |
| @@ -1149,7 +1149,8 @@ please check its value") | |||
| 1149 | (push '(visibility . icon) initial-frame-alist)) | 1149 | (push '(visibility . icon) initial-frame-alist)) |
| 1150 | ((member argi '("-nbc" "-no-blinking-cursor")) | 1150 | ((member argi '("-nbc" "-no-blinking-cursor")) |
| 1151 | (setq no-blinking-cursor t)) | 1151 | (setq no-blinking-cursor t)) |
| 1152 | ((member argi '("-dump-file" "-temacs")) ; Handled in C | 1152 | ((member argi '("-dump-file" "-temacs" "-seccomp")) |
| 1153 | ;; Handled in C | ||
| 1153 | (or argval (pop args)) | 1154 | (or argval (pop args)) |
| 1154 | (setq argval nil)) | 1155 | (setq argval nil)) |
| 1155 | ;; Push the popped arg back on the list of arguments. | 1156 | ;; Push the popped arg back on the list of arguments. |
diff --git a/src/emacs.c b/src/emacs.c index fd08667f3fd..b956e9ca34b 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -61,6 +61,13 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 61 | # include <sys/socket.h> | 61 | # include <sys/socket.h> |
| 62 | #endif | 62 | #endif |
| 63 | 63 | ||
| 64 | #ifdef HAVE_LINUX_SECCOMP_H | ||
| 65 | # include <linux/seccomp.h> | ||
| 66 | # include <linux/filter.h> | ||
| 67 | # include <sys/prctl.h> | ||
| 68 | # include <sys/syscall.h> | ||
| 69 | #endif | ||
| 70 | |||
| 64 | #ifdef HAVE_WINDOW_SYSTEM | 71 | #ifdef HAVE_WINDOW_SYSTEM |
| 65 | #include TERM_HEADER | 72 | #include TERM_HEADER |
| 66 | #endif /* HAVE_WINDOW_SYSTEM */ | 73 | #endif /* HAVE_WINDOW_SYSTEM */ |
| @@ -241,6 +248,11 @@ Initialization options:\n\ | |||
| 241 | --dump-file FILE read dumped state from FILE\n\ | 248 | --dump-file FILE read dumped state from FILE\n\ |
| 242 | ", | 249 | ", |
| 243 | #endif | 250 | #endif |
| 251 | #ifdef HAVE_LINUX_SECCOMP_H | ||
| 252 | "\ | ||
| 253 | --sandbox=FILE read Seccomp BPF filter from FILE\n\ | ||
| 254 | " | ||
| 255 | #endif | ||
| 244 | "\ | 256 | "\ |
| 245 | --no-build-details do not add build details such as time stamps\n\ | 257 | --no-build-details do not add build details such as time stamps\n\ |
| 246 | --no-desktop do not load a saved desktop\n\ | 258 | --no-desktop do not load a saved desktop\n\ |
| @@ -938,6 +950,149 @@ load_pdump (int argc, char **argv) | |||
| 938 | } | 950 | } |
| 939 | #endif /* HAVE_PDUMPER */ | 951 | #endif /* HAVE_PDUMPER */ |
| 940 | 952 | ||
| 953 | #ifdef HAVE_LINUX_SECCOMP_H | ||
| 954 | |||
| 955 | /* Wrapper function for the `seccomp' system call on GNU/Linux. This | ||
| 956 | system call usually doesn't have a wrapper function. See the | ||
| 957 | manual page of `seccomp' for the signature. */ | ||
| 958 | |||
| 959 | static int | ||
| 960 | emacs_seccomp (unsigned int operation, unsigned int flags, void *args) | ||
| 961 | { | ||
| 962 | #ifdef SYS_seccomp | ||
| 963 | return syscall (SYS_seccomp, operation, flags, args); | ||
| 964 | #else | ||
| 965 | errno = ENOSYS; | ||
| 966 | return -1; | ||
| 967 | #endif | ||
| 968 | } | ||
| 969 | |||
| 970 | /* Attempt to load Secure Computing filters from FILE. Return false | ||
| 971 | if that doesn't work for some reason. */ | ||
| 972 | |||
| 973 | static bool | ||
| 974 | load_seccomp (const char *file) | ||
| 975 | { | ||
| 976 | bool success = false; | ||
| 977 | void *buffer = NULL; | ||
| 978 | int fd | ||
| 979 | = emacs_open_noquit (file, O_RDONLY | O_CLOEXEC | O_BINARY, 0); | ||
| 980 | if (fd < 0) | ||
| 981 | { | ||
| 982 | emacs_perror ("open"); | ||
| 983 | goto out; | ||
| 984 | } | ||
| 985 | struct stat stat; | ||
| 986 | if (fstat (fd, &stat) != 0) | ||
| 987 | { | ||
| 988 | emacs_perror ("fstat"); | ||
| 989 | goto out; | ||
| 990 | } | ||
| 991 | if (! S_ISREG (stat.st_mode)) | ||
| 992 | { | ||
| 993 | fprintf (stderr, "seccomp file %s is not regular\n", file); | ||
| 994 | goto out; | ||
| 995 | } | ||
| 996 | enum | ||
| 997 | { | ||
| 998 | /* See MAX_RW_COUNT in sysdep.c. */ | ||
| 999 | #ifdef MAX_RW_COUNT | ||
| 1000 | max_read_size = MAX_RW_COUNT | ||
| 1001 | #else | ||
| 1002 | max_read_size = INT_MAX >> 18 << 18 | ||
| 1003 | #endif | ||
| 1004 | }; | ||
| 1005 | struct sock_fprog program; | ||
| 1006 | if (stat.st_size <= 0 || SIZE_MAX <= stat.st_size | ||
| 1007 | || PTRDIFF_MAX <= stat.st_size || max_read_size < stat.st_size | ||
| 1008 | || stat.st_size % sizeof *program.filter != 0) | ||
| 1009 | { | ||
| 1010 | fprintf (stderr, "seccomp filter %s has invalid size %ld\n", | ||
| 1011 | file, (long) stat.st_size); | ||
| 1012 | goto out; | ||
| 1013 | } | ||
| 1014 | size_t size = stat.st_size; | ||
| 1015 | size_t count = size / sizeof *program.filter; | ||
| 1016 | eassert (0 < count && count < SIZE_MAX); | ||
| 1017 | if (USHRT_MAX < count) | ||
| 1018 | { | ||
| 1019 | fprintf (stderr, "seccomp filter %s is too big\n", file); | ||
| 1020 | goto out; | ||
| 1021 | } | ||
| 1022 | /* Try reading one more byte to detect file size changes. */ | ||
| 1023 | buffer = malloc (size + 1); | ||
| 1024 | if (buffer == NULL) | ||
| 1025 | { | ||
| 1026 | emacs_perror ("malloc"); | ||
| 1027 | goto out; | ||
| 1028 | } | ||
| 1029 | ptrdiff_t read = emacs_read (fd, buffer, size + 1); | ||
| 1030 | if (read < 0) | ||
| 1031 | { | ||
| 1032 | emacs_perror ("read"); | ||
| 1033 | goto out; | ||
| 1034 | } | ||
| 1035 | if (read != count) | ||
| 1036 | { | ||
| 1037 | fprintf (stderr, | ||
| 1038 | "seccomp filter %s changed size while reading\n", | ||
| 1039 | file); | ||
| 1040 | goto out; | ||
| 1041 | } | ||
| 1042 | if (emacs_close (fd) < 0) | ||
| 1043 | emacs_perror ("close"); /* not a fatal error */ | ||
| 1044 | fd = -1; | ||
| 1045 | program.len = count; | ||
| 1046 | program.filter = buffer; | ||
| 1047 | |||
| 1048 | /* See man page of `seccomp' why this is necessary. Note that we | ||
| 1049 | intentionally don't check the return value: a parent process | ||
| 1050 | might have made this call before, in which case it would fail; | ||
| 1051 | or, if enabling privilege-restricting mode fails, the `seccomp' | ||
| 1052 | syscall will fail anyway. */ | ||
| 1053 | prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | ||
| 1054 | /* Install the filter. Make sure that potential other threads can't | ||
| 1055 | escape it. */ | ||
| 1056 | if (emacs_seccomp (SECCOMP_SET_MODE_FILTER, | ||
| 1057 | SECCOMP_FILTER_FLAG_TSYNC, &program) | ||
| 1058 | != 0) | ||
| 1059 | { | ||
| 1060 | emacs_perror ("seccomp"); | ||
| 1061 | goto out; | ||
| 1062 | } | ||
| 1063 | success = true; | ||
| 1064 | |||
| 1065 | out: | ||
| 1066 | if (fd < 0) | ||
| 1067 | emacs_close (fd); | ||
| 1068 | free (buffer); | ||
| 1069 | return success; | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | /* Load Secure Computing filter from file specified with the --seccomp | ||
| 1073 | option. Exit if that fails. */ | ||
| 1074 | |||
| 1075 | static void | ||
| 1076 | maybe_load_seccomp (int argc, char **argv) | ||
| 1077 | { | ||
| 1078 | int skip_args = 0; | ||
| 1079 | char *file = NULL; | ||
| 1080 | while (skip_args < argc - 1) | ||
| 1081 | { | ||
| 1082 | if (argmatch (argv, argc, "-seccomp", "--seccomp", 9, &file, | ||
| 1083 | &skip_args) | ||
| 1084 | || argmatch (argv, argc, "--", NULL, 2, NULL, &skip_args)) | ||
| 1085 | break; | ||
| 1086 | ++skip_args; | ||
| 1087 | } | ||
| 1088 | if (file == NULL) | ||
| 1089 | return; | ||
| 1090 | if (! load_seccomp (file)) | ||
| 1091 | fatal ("cannot enable seccomp filter from %s", file); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | #endif /* HAVE_LINUX_SECCOMP_H */ | ||
| 1095 | |||
| 941 | int | 1096 | int |
| 942 | main (int argc, char **argv) | 1097 | main (int argc, char **argv) |
| 943 | { | 1098 | { |
| @@ -945,6 +1100,13 @@ main (int argc, char **argv) | |||
| 945 | for pointers. */ | 1100 | for pointers. */ |
| 946 | void *stack_bottom_variable; | 1101 | void *stack_bottom_variable; |
| 947 | 1102 | ||
| 1103 | /* First, check whether we should apply a seccomp filter. This | ||
| 1104 | should come at the very beginning to allow the filter to protect | ||
| 1105 | the initialization phase. */ | ||
| 1106 | #ifdef HAVE_LINUX_SECCOMP_H | ||
| 1107 | maybe_load_seccomp (argc, argv); | ||
| 1108 | #endif | ||
| 1109 | |||
| 948 | bool no_loadup = false; | 1110 | bool no_loadup = false; |
| 949 | char *junk = 0; | 1111 | char *junk = 0; |
| 950 | char *dname_arg = 0; | 1112 | char *dname_arg = 0; |
| @@ -2133,12 +2295,15 @@ static const struct standard_args standard_args[] = | |||
| 2133 | { "-color", "--color", 5, 0}, | 2295 | { "-color", "--color", 5, 0}, |
| 2134 | { "-no-splash", "--no-splash", 3, 0 }, | 2296 | { "-no-splash", "--no-splash", 3, 0 }, |
| 2135 | { "-no-desktop", "--no-desktop", 3, 0 }, | 2297 | { "-no-desktop", "--no-desktop", 3, 0 }, |
| 2136 | /* The following two must be just above the file-name args, to get | 2298 | /* The following three must be just above the file-name args, to get |
| 2137 | them out of our way, but without mixing them with file names. */ | 2299 | them out of our way, but without mixing them with file names. */ |
| 2138 | { "-temacs", "--temacs", 1, 1 }, | 2300 | { "-temacs", "--temacs", 1, 1 }, |
| 2139 | #ifdef HAVE_PDUMPER | 2301 | #ifdef HAVE_PDUMPER |
| 2140 | { "-dump-file", "--dump-file", 1, 1 }, | 2302 | { "-dump-file", "--dump-file", 1, 1 }, |
| 2141 | #endif | 2303 | #endif |
| 2304 | #ifdef HAVE_LINUX_SECCOMP_H | ||
| 2305 | { "-seccomp", "--seccomp", 1, 1 }, | ||
| 2306 | #endif | ||
| 2142 | #ifdef HAVE_NS | 2307 | #ifdef HAVE_NS |
| 2143 | { "-NSAutoLaunch", 0, 5, 1 }, | 2308 | { "-NSAutoLaunch", 0, 5, 1 }, |
| 2144 | { "-NXAutoLaunch", 0, 5, 1 }, | 2309 | { "-NXAutoLaunch", 0, 5, 1 }, |
diff --git a/test/src/emacs-tests.el b/test/src/emacs-tests.el new file mode 100644 index 00000000000..7618a9c6752 --- /dev/null +++ b/test/src/emacs-tests.el | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | ;;; emacs-tests.el --- unit tests for emacs.c -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2020 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published | ||
| 9 | ;; by the Free Software Foundation, either version 3 of the License, | ||
| 10 | ;; or (at your option) any later version. | ||
| 11 | |||
| 12 | ;; GNU Emacs is distributed in the hope that it will be useful, but | ||
| 13 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | ;; General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 19 | |||
| 20 | ;;; Commentary: | ||
| 21 | |||
| 22 | ;; Unit tests for src/emacs.c. | ||
| 23 | |||
| 24 | ;;; Code: | ||
| 25 | |||
| 26 | (require 'cl-lib) | ||
| 27 | (require 'ert) | ||
| 28 | (require 'rx) | ||
| 29 | |||
| 30 | (ert-deftest emacs-tests/seccomp/absent-file () | ||
| 31 | (skip-unless (string-match-p (rx bow "SECCOMP" eow) | ||
| 32 | system-configuration-features)) | ||
| 33 | (let ((emacs | ||
| 34 | (expand-file-name invocation-name invocation-directory)) | ||
| 35 | (process-environment nil)) | ||
| 36 | (skip-unless (file-executable-p emacs)) | ||
| 37 | (should-not (file-exists-p "/does-not-exist.bpf")) | ||
| 38 | (should-not | ||
| 39 | (eql (call-process emacs nil nil nil | ||
| 40 | "--quick" "--batch" | ||
| 41 | "--seccomp=/does-not-exist.bpf") | ||
| 42 | 0)))) | ||
| 43 | |||
| 44 | (cl-defmacro emacs-tests--with-temp-file | ||
| 45 | (var (prefix &optional suffix text) &rest body) | ||
| 46 | "Evaluate BODY while a new temporary file exists. | ||
| 47 | Bind VAR to the name of the file. Pass PREFIX, SUFFIX, and TEXT | ||
| 48 | to `make-temp-file', which see." | ||
| 49 | (declare (indent 2) (debug (symbolp (form form form) body))) | ||
| 50 | (cl-check-type var symbol) | ||
| 51 | ;; Use an uninterned symbol so that the code still works if BODY | ||
| 52 | ;; changes VAR. | ||
| 53 | (let ((filename (make-symbol "filename"))) | ||
| 54 | `(let ((,filename (make-temp-file ,prefix nil ,suffix ,text))) | ||
| 55 | (unwind-protect | ||
| 56 | (let ((,var ,filename)) | ||
| 57 | ,@body) | ||
| 58 | (delete-file ,filename))))) | ||
| 59 | |||
| 60 | (ert-deftest emacs-tests/seccomp/empty-file () | ||
| 61 | (skip-unless (string-match-p (rx bow "SECCOMP" eow) | ||
| 62 | system-configuration-features)) | ||
| 63 | (let ((emacs | ||
| 64 | (expand-file-name invocation-name invocation-directory)) | ||
| 65 | (process-environment nil)) | ||
| 66 | (skip-unless (file-executable-p emacs)) | ||
| 67 | (emacs-tests--with-temp-file filter ("seccomp-invalid-" ".bpf") | ||
| 68 | ;; The --seccomp option is processed early, without filename | ||
| 69 | ;; handlers. Therefore remote or quoted filenames wouldn't | ||
| 70 | ;; work. | ||
| 71 | (should-not (file-remote-p filter)) | ||
| 72 | (cl-callf file-name-unquote filter) | ||
| 73 | ;; According to the Seccomp man page, a filter must have at | ||
| 74 | ;; least one element, so Emacs should reject an empty file. | ||
| 75 | (should-not | ||
| 76 | (eql (call-process emacs nil nil nil | ||
| 77 | "--quick" "--batch" | ||
| 78 | (concat "--seccomp=" filter)) | ||
| 79 | 0))))) | ||
| 80 | |||
| 81 | (ert-deftest emacs-tests/seccomp/file-too-large () | ||
| 82 | (skip-unless (string-match-p (rx bow "SECCOMP" eow) | ||
| 83 | system-configuration-features)) | ||
| 84 | (let ((emacs | ||
| 85 | (expand-file-name invocation-name invocation-directory)) | ||
| 86 | (process-environment nil) | ||
| 87 | ;; This value should be correct on all supported systems. | ||
| 88 | (ushort-max #xFFFF) | ||
| 89 | ;; Either 8 or 16, but 16 should be large enough in all cases. | ||
| 90 | (filter-size 16)) | ||
| 91 | (skip-unless (file-executable-p emacs)) | ||
| 92 | (emacs-tests--with-temp-file | ||
| 93 | filter ("seccomp-too-large-" ".bpf" | ||
| 94 | (make-string (* (1+ ushort-max) filter-size) ?a)) | ||
| 95 | ;; The --seccomp option is processed early, without filename | ||
| 96 | ;; handlers. Therefore remote or quoted filenames wouldn't | ||
| 97 | ;; work. | ||
| 98 | (should-not (file-remote-p filter)) | ||
| 99 | (cl-callf file-name-unquote filter) | ||
| 100 | ;; The filter count must fit into an `unsigned short'. A bigger | ||
| 101 | ;; file should be rejected. | ||
| 102 | (should-not | ||
| 103 | (eql (call-process emacs nil nil nil | ||
| 104 | "--quick" "--batch" | ||
| 105 | (concat "--seccomp=" filter)) | ||
| 106 | 0))))) | ||
| 107 | |||
| 108 | (ert-deftest emacs-tests/seccomp/invalid-file-size () | ||
| 109 | (skip-unless (string-match-p (rx bow "SECCOMP" eow) | ||
| 110 | system-configuration-features)) | ||
| 111 | (let ((emacs | ||
| 112 | (expand-file-name invocation-name invocation-directory)) | ||
| 113 | (process-environment nil)) | ||
| 114 | (skip-unless (file-executable-p emacs)) | ||
| 115 | (emacs-tests--with-temp-file filter ("seccomp-invalid-" ".bpf" | ||
| 116 | "123456") | ||
| 117 | ;; The --seccomp option is processed early, without filename | ||
| 118 | ;; handlers. Therefore remote or quoted filenames wouldn't | ||
| 119 | ;; work. | ||
| 120 | (should-not (file-remote-p filter)) | ||
| 121 | (cl-callf file-name-unquote filter) | ||
| 122 | ;; The Seccomp filter file must have a file size that's a | ||
| 123 | ;; multiple of the size of struct sock_filter, which is 8 or 16, | ||
| 124 | ;; but never 6. | ||
| 125 | (should-not | ||
| 126 | (eql (call-process emacs nil nil nil | ||
| 127 | "--quick" "--batch" | ||
| 128 | (concat "--seccomp=" filter)) | ||
| 129 | 0))))) | ||
| 130 | |||
| 131 | ;;; emacs-tests.el ends here | ||