[PATCH gnupg v12] Disable CPU speculation-related misfeatures
Guido Trentalancia
guido at trentalancia.com
Thu Jul 10 12:41:39 CEST 2025
I have attached a v12 patch which restores the old signal action after
calling prctl().
Please note that sigaction() is used in several other source files
within gnupg.
I have tested the patch and the logging functions work as I would
expect. They might need to be initialized before early_system_init(),
that's another problem that has nothing to do with this patch.
The most important point is that the security vulnerabilities addressed
by this patch are not being tackled, while a multitude of supposed
minor imperfections or different coding styles are being pointed out as
the culprit of all problems: this is out of touch with reality !
The time is can spend on improving this patch is limited, I would
recommend the real underlying security problem is tackled without
hesitation since such security issues have been affecting gnupg since
2017, leaving the program vulnerable to data leaks for about 8 years !
That is the real problem !!
Patch v12 follows...
common: Disable CPU speculative execution security
vulnerabilities (CVE-2018-3639 aka Spectre variant 4,
CVE-2017-5715 and optionally CVE-2020-0550)
* configure.ac: add a new L1D Cache flushing option
(--enable-l1d-cache-flushing) to fix CVE-2020-0550
and check for sys/prctl.h on Linux systems
* common/init.c (early_system_init): Disable CPU
speculative execution security vulnerabilities
potentially causing data leaks:
- Speculative Store Bypass (always disabled)
- Indirect Branch Speculation (always disabled)
- Flush L1D Cache on context switch out of the
task (use the --enable-l1d-cache-flushing
configure option and "nosmt l1d_flush=on" on the
boot command line to mitigate the vulnerability)
For further information see the kernel documentation:
Documentation/userspace-api/spec_ctrl.rst
Documentation/admin-guide/hw-vuln/l1d_flush.rst
Signed-off-by: Guido Trentalancia <guido at trentalancia.com>
diff -pru a/common/init.c b/common/init.c
--- a/common/init.c 2025-05-25 15:43:45.871984100 +0200
+++ b/common/init.c 2025-07-09 13:39:44.036998821 +0200
@@ -29,6 +29,14 @@
#include <config.h>
+#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H)
+# include <sys/prctl.h>
+#endif
+
+#if defined(ENABLE_L1D_CACHE_FLUSH)
+# include <signal.h>
+#endif
+
#ifdef HAVE_W32_SYSTEM
# if _WIN32_WINNT < 0x0600
# define _WIN32_WINNT 0x0600 /* Required for SetProcessDEPPolicy. */
@@ -128,10 +136,68 @@ writestring_via_estream (int mode, const
}
+#ifdef ENABLE_L1D_CACHE_FLUSH
+void sigbus_handler (int signo)
+{
+ if (signo == SIGBUS)
+ {
+ log_fatal ("Level 1 Data Cache flushing requires the \"nosmt\" boot parameter.\n");
+ exit (SIGBUS);
+ }
+}
+#endif
+
+
/* This function should be the first called after main. */
void
early_system_init (void)
{
+#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H)
+
+/* Disable CPU speculative execution security vulnerabilities
+ * causing data leaks: see the Linux kernel documentation
+ * Documentation/userspace-api/spec_ctrl.rst
+ *
+ * - Speculative Store Bypass (CVE-2018-3639, always
+ * disabled)
+ * - Indirect Branch Speculation (CVE-2017-5715, always
+ * disabled)
+ * - Flush L1D Cache on context switch out of the task (it
+ * requires the "nosmt l1d_flush=on" kernel boot parameter)
+ */
+#ifdef PR_SPEC_STORE_BYPASS
+ prctl (PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0);
+#endif
+
+#ifdef PR_SPEC_INDIRECT_BRANCH
+ prctl (PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);
+#endif
+
+#if defined(ENABLE_L1D_CACHE_FLUSH) && defined(PR_SPEC_L1D_FLUSH)
+ struct sigaction old_action, new_action;
+
+ new_action.sa_handler = sigbus_handler;
+ sigemptyset (&new_action.sa_mask);
+ new_action.sa_flags = 0;
+
+ sigaction (SIGBUS, NULL, &old_action);
+ if (old_action.sa_handler != SIG_IGN)
+ {
+ if (sigaction (SIGBUS, &new_action, NULL) == -1)
+ {
+ log_info ("Warning: cannot catch the SIGBUS signal.\n");
+ }
+ }
+
+ if (prctl (PR_SET_SPECULATION_CTRL, PR_SPEC_L1D_FLUSH, PR_SPEC_ENABLE, 0, 0) < 0)
+ {
+ log_info ("Warning: Level 1 Data Cache flushing requires the \"l1d_flush=on\" boot parameter.\n");
+ }
+
+ sigaction (SIGBUS, &old_action, NULL);
+#endif
+
+#endif /* __linux__ && HAVE_SYS_PRCTL_H */
}
diff -pru a/configure.ac b/configure.ac
--- a/configure.ac 2025-07-06 18:01:54.128546282 +0200
+++ b/configure.ac 2025-07-08 21:32:32.674405293 +0200
@@ -244,6 +244,16 @@ AC_ARG_ENABLE(selinux-support,
AC_MSG_RESULT($selinux_support)
+# Fix security vulnerability CVE-2020-0550 by enabling
+# Level 1 Data Cache flushing on context switch.
+AC_MSG_CHECKING([whether Level 1 Data Cache is flushed on context switch])
+AC_ARG_ENABLE(l1d-cache-flushing,
+ AS_HELP_STRING([--enable-l1d-cache-flushing],
+ [enable L1D cache flushing]),
+ l1d_cache_flushing=$enableval, l1d_cache_flushing=no)
+AC_MSG_RESULT($l1d_cache_flushing)
+
+
AC_MSG_CHECKING([whether to allocate extra secure memory])
AC_ARG_ENABLE(large-secmem,
AS_HELP_STRING([--enable-large-secmem],
@@ -1313,6 +1323,16 @@ fi
#
+# Level 1 Data Cache flushing on context switch (CVE-2020-0550)
+#
+if test "$l1d_cache_flushing" = yes ; then
+ AC_DEFINE(ENABLE_L1D_CACHE_FLUSH,1,
+ [Define to enable Layer 1 Data Cache flushing])
+ AC_CHECK_HEADERS([signal.h])
+fi
+
+
+#
# Checks for header files.
#
AC_MSG_NOTICE([checking for header files])
@@ -1322,6 +1342,13 @@ AC_CHECK_HEADERS([unistd.h langinfo.h te
ucred.h sys/ucred.h sys/sysmacros.h sys/mkdev.h])
+# See whether libc supports the prctl()
+case "${host}" in
+ *-*-linux*)
+ AC_CHECK_HEADERS([sys/prctl.h])
+ ;;
+esac
+
#
# Checks for typedefs, structures, and compiler characteristics.
#
On Wed, 09/07/2025 at 22.40 -0500, Jacob Bachmeyer wrote:
> On 7/9/25 07:22, Guido Trentalancia wrote:
> > A new v11 patch has created to use sigaction() instead of signal()
> > to
> > deal with the SIGBUS signal. A few other cosmetic changes have been
> > introduced.
>
> This v11 does not actually fix the problem with the patch in v10:
> you
> now *obtain* the previous SIGBUS handler but you never *restore* it
> after prctl() returns. (You need a second (or third) sigaction()
> call
> to reinstall the old handler after calling prctl(). A single
> sigaction() call could both set a new handler *and* return the old
> handler before calling prctl().)
>
> Further, as Werner Koch, who is the GPG maintainer and therefore the
> person whose approval you need for this patch to be accepted, noted
> in
> another reply to the v10 patch, signal handling in GnuPG uses the
> nPth
> signal features, with which I am unfamiliar. You will need to find
> those and use them instead of signal() or sigaction().
>
> Also, as Werner Koch noted, you cannot call log_* functions in a
> signal
> handler, nor can you call them from code in early_system_init(),
> although the latter appears to "happen to work" *if* you have
> actually
> been testing your patch.
>
> Are you actually testing the error paths in your patch? Are you
> testing
> the patch at all?
>
>
> -- Jacob
>
More information about the Gnupg-devel
mailing list