summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--0000_README4
-rw-r--r--1087_linux-5.15.88.patch656
2 files changed, 660 insertions, 0 deletions
diff --git a/0000_README b/0000_README
index 554b9da3..3f36a0d8 100644
--- a/0000_README
+++ b/0000_README
@@ -391,6 +391,10 @@ Patch: 1086_linux-5.15.87.patch
From: http://www.kernel.org
Desc: Linux 5.15.87
+Patch: 1087_linux-5.15.88.patch
+From: http://www.kernel.org
+Desc: Linux 5.15.88
+
Patch: 1500_XATTR_USER_PREFIX.patch
From: https://bugs.gentoo.org/show_bug.cgi?id=470644
Desc: Support for namespace user.pax.* on tmpfs.
diff --git a/1087_linux-5.15.88.patch b/1087_linux-5.15.88.patch
new file mode 100644
index 00000000..6952d95a
--- /dev/null
+++ b/1087_linux-5.15.88.patch
@@ -0,0 +1,656 @@
+diff --git a/Makefile b/Makefile
+index 6a9589c7b1bc0..f7ff35b0cf6fc 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ VERSION = 5
+ PATCHLEVEL = 15
+-SUBLEVEL = 87
++SUBLEVEL = 88
+ EXTRAVERSION =
+ NAME = Trick or Treat
+
+diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h
+index 9e3c010c0f61e..5f1f3eea5aa5f 100644
+--- a/arch/parisc/include/uapi/asm/mman.h
++++ b/arch/parisc/include/uapi/asm/mman.h
+@@ -49,31 +49,30 @@
+ #define MADV_DONTFORK 10 /* don't inherit across fork */
+ #define MADV_DOFORK 11 /* do inherit across fork */
+
+-#define MADV_COLD 20 /* deactivate these pages */
+-#define MADV_PAGEOUT 21 /* reclaim these pages */
++#define MADV_MERGEABLE 12 /* KSM may merge identical pages */
++#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
+
+-#define MADV_POPULATE_READ 22 /* populate (prefault) page tables readable */
+-#define MADV_POPULATE_WRITE 23 /* populate (prefault) page tables writable */
++#define MADV_HUGEPAGE 14 /* Worth backing with hugepages */
++#define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */
+
+-#define MADV_MERGEABLE 65 /* KSM may merge identical pages */
+-#define MADV_UNMERGEABLE 66 /* KSM may not merge identical pages */
++#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump,
++ overrides the coredump filter bits */
++#define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */
+
+-#define MADV_HUGEPAGE 67 /* Worth backing with hugepages */
+-#define MADV_NOHUGEPAGE 68 /* Not worth backing with hugepages */
++#define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */
++#define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */
+
+-#define MADV_DONTDUMP 69 /* Explicity exclude from the core dump,
+- overrides the coredump filter bits */
+-#define MADV_DODUMP 70 /* Clear the MADV_NODUMP flag */
++#define MADV_COLD 20 /* deactivate these pages */
++#define MADV_PAGEOUT 21 /* reclaim these pages */
+
+-#define MADV_WIPEONFORK 71 /* Zero memory on fork, child only */
+-#define MADV_KEEPONFORK 72 /* Undo MADV_WIPEONFORK */
++#define MADV_POPULATE_READ 22 /* populate (prefault) page tables readable */
++#define MADV_POPULATE_WRITE 23 /* populate (prefault) page tables writable */
+
+ #define MADV_HWPOISON 100 /* poison a page for testing */
+ #define MADV_SOFT_OFFLINE 101 /* soft offline page for testing */
+
+ /* compatibility flags */
+ #define MAP_FILE 0
+-#define MAP_VARIABLE 0
+
+ #define PKEY_DISABLE_ACCESS 0x1
+ #define PKEY_DISABLE_WRITE 0x2
+diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c
+index 5f12537318ab2..31950882e272f 100644
+--- a/arch/parisc/kernel/sys_parisc.c
++++ b/arch/parisc/kernel/sys_parisc.c
+@@ -463,3 +463,30 @@ asmlinkage long parisc_inotify_init1(int flags)
+ flags = FIX_O_NONBLOCK(flags);
+ return sys_inotify_init1(flags);
+ }
++
++/*
++ * madvise() wrapper
++ *
++ * Up to kernel v6.1 parisc has different values than all other
++ * platforms for the MADV_xxx flags listed below.
++ * To keep binary compatibility with existing userspace programs
++ * translate the former values to the new values.
++ *
++ * XXX: Remove this wrapper in year 2025 (or later)
++ */
++
++asmlinkage notrace long parisc_madvise(unsigned long start, size_t len_in, int behavior)
++{
++ switch (behavior) {
++ case 65: behavior = MADV_MERGEABLE; break;
++ case 66: behavior = MADV_UNMERGEABLE; break;
++ case 67: behavior = MADV_HUGEPAGE; break;
++ case 68: behavior = MADV_NOHUGEPAGE; break;
++ case 69: behavior = MADV_DONTDUMP; break;
++ case 70: behavior = MADV_DODUMP; break;
++ case 71: behavior = MADV_WIPEONFORK; break;
++ case 72: behavior = MADV_KEEPONFORK; break;
++ }
++
++ return sys_madvise(start, len_in, behavior);
++}
+diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
+index c23f4fa170051..50c759f11c25d 100644
+--- a/arch/parisc/kernel/syscalls/syscall.tbl
++++ b/arch/parisc/kernel/syscalls/syscall.tbl
+@@ -131,7 +131,7 @@
+ 116 common sysinfo sys_sysinfo compat_sys_sysinfo
+ 117 common shutdown sys_shutdown
+ 118 common fsync sys_fsync
+-119 common madvise sys_madvise
++119 common madvise parisc_madvise
+ 120 common clone sys_clone_wrapper
+ 121 common setdomainname sys_setdomainname
+ 122 common sendfile sys_sendfile compat_sys_sendfile
+diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h
+index 109dfcc75299d..d91df71f60fb1 100644
+--- a/arch/x86/include/asm/fpu/xstate.h
++++ b/arch/x86/include/asm/fpu/xstate.h
+@@ -136,8 +136,8 @@ extern void __init update_regset_xstate_info(unsigned int size,
+
+ void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);
+ int xfeature_size(int xfeature_nr);
+-int copy_uabi_from_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
+-int copy_sigframe_from_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);
++int copy_uabi_from_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf, u32 *pkru);
++int copy_sigframe_from_user_to_xstate(struct task_struct *tsk, const void __user *ubuf);
+
+ void xsaves(struct xregs_state *xsave, u64 mask);
+ void xrstors(struct xregs_state *xsave, u64 mask);
+diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c
+index 125cbbe10fefa..bd243ae57680e 100644
+--- a/arch/x86/kernel/fpu/regset.c
++++ b/arch/x86/kernel/fpu/regset.c
+@@ -163,7 +163,7 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
+ }
+
+ fpu_force_restore(fpu);
+- ret = copy_uabi_from_kernel_to_xstate(&fpu->state.xsave, kbuf ?: tmpbuf);
++ ret = copy_uabi_from_kernel_to_xstate(&fpu->state.xsave, kbuf ?: tmpbuf, &target->thread.pkru);
+
+ out:
+ vfree(tmpbuf);
+diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
+index 7f71bd4dcd0d6..7f76cb099e66a 100644
+--- a/arch/x86/kernel/fpu/signal.c
++++ b/arch/x86/kernel/fpu/signal.c
+@@ -370,7 +370,7 @@ static int __fpu_restore_sig(void __user *buf, void __user *buf_fx,
+ fpregs_unlock();
+
+ if (use_xsave() && !fx_only) {
+- ret = copy_sigframe_from_user_to_xstate(&fpu->state.xsave, buf_fx);
++ ret = copy_sigframe_from_user_to_xstate(tsk, buf_fx);
+ if (ret)
+ return ret;
+ } else {
+diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
+index c8def1b7f8fba..8bbf37c0bebe2 100644
+--- a/arch/x86/kernel/fpu/xstate.c
++++ b/arch/x86/kernel/fpu/xstate.c
+@@ -1091,8 +1091,31 @@ static int copy_from_buffer(void *dst, unsigned int offset, unsigned int size,
+ }
+
+
++/**
++ * copy_uabi_to_xstate - Copy a UABI format buffer to the kernel xstate
++ * @fpstate: The fpstate buffer to copy to
++ * @kbuf: The UABI format buffer, if it comes from the kernel
++ * @ubuf: The UABI format buffer, if it comes from userspace
++ * @pkru: The location to write the PKRU value to
++ *
++ * Converts from the UABI format into the kernel internal hardware
++ * dependent format.
++ *
++ * This function ultimately has two different callers with distinct PKRU
++ * behavior.
++ * 1. When called from sigreturn the PKRU register will be restored from
++ * @fpstate via an XRSTOR. Correctly copying the UABI format buffer to
++ * @fpstate is sufficient to cover this case, but the caller will also
++ * pass a pointer to the thread_struct's pkru field in @pkru and updating
++ * it is harmless.
++ * 2. When called from ptrace the PKRU register will be restored from the
++ * thread_struct's pkru field. A pointer to that is passed in @pkru.
++ * The kernel will restore it manually, so the XRSTOR behavior that resets
++ * the PKRU register to the hardware init value (0) if the corresponding
++ * xfeatures bit is not set is emulated here.
++ */
+ static int copy_uabi_to_xstate(struct xregs_state *xsave, const void *kbuf,
+- const void __user *ubuf)
++ const void __user *ubuf, u32 *pkru)
+ {
+ unsigned int offset, size;
+ struct xstate_header hdr;
+@@ -1140,6 +1163,14 @@ static int copy_uabi_to_xstate(struct xregs_state *xsave, const void *kbuf,
+ }
+ }
+
++ if (hdr.xfeatures & XFEATURE_MASK_PKRU) {
++ struct pkru_state *xpkru;
++
++ xpkru = __raw_xsave_addr(xsave, XFEATURE_PKRU);
++ *pkru = xpkru->pkru;
++ } else
++ *pkru = 0;
++
+ /*
+ * The state that came in from userspace was user-state only.
+ * Mask all the user states out of 'xfeatures':
+@@ -1159,9 +1190,9 @@ static int copy_uabi_to_xstate(struct xregs_state *xsave, const void *kbuf,
+ * format and copy to the target thread. This is called from
+ * xstateregs_set().
+ */
+-int copy_uabi_from_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf)
++int copy_uabi_from_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf, u32 *pkru)
+ {
+- return copy_uabi_to_xstate(xsave, kbuf, NULL);
++ return copy_uabi_to_xstate(xsave, kbuf, NULL, pkru);
+ }
+
+ /*
+@@ -1169,10 +1200,10 @@ int copy_uabi_from_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf)
+ * XSAVE[S] format and copy to the target thread. This is called from the
+ * sigreturn() and rt_sigreturn() system calls.
+ */
+-int copy_sigframe_from_user_to_xstate(struct xregs_state *xsave,
++int copy_sigframe_from_user_to_xstate(struct task_struct *tsk,
+ const void __user *ubuf)
+ {
+- return copy_uabi_to_xstate(xsave, NULL, ubuf);
++ return copy_uabi_to_xstate(&tsk->thread.fpu.state.xsave, NULL, ubuf, &tsk->thread.pkru);
+ }
+
+ static bool validate_xsaves_xrstors(u64 mask)
+diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
+index 595430aedc0da..fc311df9f1c9d 100644
+--- a/drivers/tty/serial/fsl_lpuart.c
++++ b/drivers/tty/serial/fsl_lpuart.c
+@@ -2784,9 +2784,9 @@ static int lpuart_probe(struct platform_device *pdev)
+ return 0;
+
+ failed_irq_request:
+-failed_get_rs485:
+ uart_remove_one_port(&lpuart_reg, &sport->port);
+ failed_attach_port:
++failed_get_rs485:
+ failed_reset:
+ lpuart_disable_clks(sport);
+ return ret;
+diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
+index 5f8f0a90ce551..45b721abaa2f5 100644
+--- a/drivers/tty/serial/serial_core.c
++++ b/drivers/tty/serial/serial_core.c
+@@ -2225,7 +2225,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
+
+ spin_lock_irq(&uport->lock);
+ ops->stop_tx(uport);
+- ops->set_mctrl(uport, 0);
++ if (!(uport->rs485.flags & SER_RS485_ENABLED))
++ ops->set_mctrl(uport, 0);
+ ops->stop_rx(uport);
+ spin_unlock_irq(&uport->lock);
+
+diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
+index 8039097546dea..a86140ff093cb 100644
+--- a/net/ipv4/inet_connection_sock.c
++++ b/net/ipv4/inet_connection_sock.c
+@@ -1040,11 +1040,25 @@ void inet_csk_prepare_forced_close(struct sock *sk)
+ }
+ EXPORT_SYMBOL(inet_csk_prepare_forced_close);
+
++static int inet_ulp_can_listen(const struct sock *sk)
++{
++ const struct inet_connection_sock *icsk = inet_csk(sk);
++
++ if (icsk->icsk_ulp_ops && !icsk->icsk_ulp_ops->clone)
++ return -EINVAL;
++
++ return 0;
++}
++
+ int inet_csk_listen_start(struct sock *sk, int backlog)
+ {
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+- int err = -EADDRINUSE;
++ int err;
++
++ err = inet_ulp_can_listen(sk);
++ if (unlikely(err))
++ return err;
+
+ reqsk_queue_alloc(&icsk->icsk_accept_queue);
+
+diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c
+index 7c27aa629af19..b5d707a5a31b8 100644
+--- a/net/ipv4/tcp_ulp.c
++++ b/net/ipv4/tcp_ulp.c
+@@ -136,6 +136,10 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
+ if (icsk->icsk_ulp_ops)
+ goto out_err;
+
++ err = -EINVAL;
++ if (!ulp_ops->clone && sk->sk_state == TCP_LISTEN)
++ goto out_err;
++
+ err = ulp_ops->init(sk);
+ if (err)
+ goto out_err;
+diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
+index 5ab20c764aa5b..02f62008e468f 100644
+--- a/net/sched/sch_api.c
++++ b/net/sched/sch_api.c
+@@ -1114,6 +1114,11 @@ skip:
+ return -ENOENT;
+ }
+
++ if (new && new->ops == &noqueue_qdisc_ops) {
++ NL_SET_ERR_MSG(extack, "Cannot assign noqueue to a class");
++ return -EINVAL;
++ }
++
+ err = cops->graft(parent, cl, new, &old, extack);
+ if (err)
+ return err;
+diff --git a/sound/core/control.c b/sound/core/control.c
+index f66fe4be30d35..b83ec284d6114 100644
+--- a/sound/core/control.c
++++ b/sound/core/control.c
+@@ -1067,14 +1067,19 @@ static int snd_ctl_elem_read(struct snd_card *card,
+ const u32 pattern = 0xdeadbeef;
+ int ret;
+
++ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &control->id);
+- if (kctl == NULL)
+- return -ENOENT;
++ if (kctl == NULL) {
++ ret = -ENOENT;
++ goto unlock;
++ }
+
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+- if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL)
+- return -EPERM;
++ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) {
++ ret = -EPERM;
++ goto unlock;
++ }
+
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+
+@@ -1084,7 +1089,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
+ info.id = control->id;
+ ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
+ if (ret < 0)
+- return ret;
++ goto unlock;
+ #endif
+
+ if (!snd_ctl_skip_validation(&info))
+@@ -1094,7 +1099,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
+ ret = kctl->get(kctl, control);
+ snd_power_unref(card);
+ if (ret < 0)
+- return ret;
++ goto unlock;
+ if (!snd_ctl_skip_validation(&info) &&
+ sanity_check_elem_value(card, control, &info, pattern) < 0) {
+ dev_err(card->dev,
+@@ -1102,8 +1107,11 @@ static int snd_ctl_elem_read(struct snd_card *card,
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index);
+- return -EINVAL;
++ ret = -EINVAL;
++ goto unlock;
+ }
++unlock:
++ up_read(&card->controls_rwsem);
+ return ret;
+ }
+
+@@ -1117,9 +1125,7 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
+ if (IS_ERR(control))
+ return PTR_ERR(control);
+
+- down_read(&card->controls_rwsem);
+ result = snd_ctl_elem_read(card, control);
+- up_read(&card->controls_rwsem);
+ if (result < 0)
+ goto error;
+
+diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
+index 5bf0597ce38b8..8ed66c416c0e7 100644
+--- a/sound/pci/hda/patch_hdmi.c
++++ b/sound/pci/hda/patch_hdmi.c
+@@ -1963,6 +1963,7 @@ static const struct snd_pci_quirk force_connect_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
++ SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1),
+ SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
+ {}
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 642e212278aca..47fdf7dc24723 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -8909,6 +8909,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
++ SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+diff --git a/tools/arch/parisc/include/uapi/asm/mman.h b/tools/arch/parisc/include/uapi/asm/mman.h
+index 506c06a6536fb..4cc88a642e106 100644
+--- a/tools/arch/parisc/include/uapi/asm/mman.h
++++ b/tools/arch/parisc/include/uapi/asm/mman.h
+@@ -1,20 +1,20 @@
+ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+ #ifndef TOOLS_ARCH_PARISC_UAPI_ASM_MMAN_FIX_H
+ #define TOOLS_ARCH_PARISC_UAPI_ASM_MMAN_FIX_H
+-#define MADV_DODUMP 70
++#define MADV_DODUMP 17
+ #define MADV_DOFORK 11
+-#define MADV_DONTDUMP 69
++#define MADV_DONTDUMP 16
+ #define MADV_DONTFORK 10
+ #define MADV_DONTNEED 4
+ #define MADV_FREE 8
+-#define MADV_HUGEPAGE 67
+-#define MADV_MERGEABLE 65
+-#define MADV_NOHUGEPAGE 68
++#define MADV_HUGEPAGE 14
++#define MADV_MERGEABLE 12
++#define MADV_NOHUGEPAGE 15
+ #define MADV_NORMAL 0
+ #define MADV_RANDOM 1
+ #define MADV_REMOVE 9
+ #define MADV_SEQUENTIAL 2
+-#define MADV_UNMERGEABLE 66
++#define MADV_UNMERGEABLE 13
+ #define MADV_WILLNEED 3
+ #define MAP_ANONYMOUS 0x10
+ #define MAP_DENYWRITE 0x0800
+diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
+index b3480bc33fe84..baa7c63014004 100644
+--- a/tools/perf/bench/bench.h
++++ b/tools/perf/bench/bench.h
+@@ -10,25 +10,13 @@ extern struct timeval bench__start, bench__end, bench__runtime;
+ * The madvise transparent hugepage constants were added in glibc
+ * 2.13. For compatibility with older versions of glibc, define these
+ * tokens if they are not already defined.
+- *
+- * PA-RISC uses different madvise values from other architectures and
+- * needs to be special-cased.
+ */
+-#ifdef __hppa__
+-# ifndef MADV_HUGEPAGE
+-# define MADV_HUGEPAGE 67
+-# endif
+-# ifndef MADV_NOHUGEPAGE
+-# define MADV_NOHUGEPAGE 68
+-# endif
+-#else
+ # ifndef MADV_HUGEPAGE
+ # define MADV_HUGEPAGE 14
+ # endif
+ # ifndef MADV_NOHUGEPAGE
+ # define MADV_NOHUGEPAGE 15
+ # endif
+-#endif
+
+ int bench_numa(int argc, const char **argv);
+ int bench_sched_messaging(int argc, const char **argv);
+diff --git a/tools/testing/selftests/vm/pkey-x86.h b/tools/testing/selftests/vm/pkey-x86.h
+index e4a4ce2b826d2..ea8c8afbcdbb3 100644
+--- a/tools/testing/selftests/vm/pkey-x86.h
++++ b/tools/testing/selftests/vm/pkey-x86.h
+@@ -119,6 +119,18 @@ static inline int cpu_has_pkeys(void)
+ return 1;
+ }
+
++static inline int cpu_max_xsave_size(void)
++{
++ unsigned long XSTATE_CPUID = 0xd;
++ unsigned int eax;
++ unsigned int ebx;
++ unsigned int ecx;
++ unsigned int edx;
++
++ __cpuid_count(XSTATE_CPUID, 0, eax, ebx, ecx, edx);
++ return ecx;
++}
++
+ static inline u32 pkey_bit_position(int pkey)
+ {
+ return pkey * PKEY_BITS_PER_PKEY;
+diff --git a/tools/testing/selftests/vm/protection_keys.c b/tools/testing/selftests/vm/protection_keys.c
+index 2d0ae88665db0..2d48272b2463e 100644
+--- a/tools/testing/selftests/vm/protection_keys.c
++++ b/tools/testing/selftests/vm/protection_keys.c
+@@ -18,12 +18,13 @@
+ * do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks
+ *
+ * Compile like this:
+- * gcc -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
+- * gcc -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
++ * gcc -mxsave -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
++ * gcc -mxsave -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
+ */
+ #define _GNU_SOURCE
+ #define __SANE_USERSPACE_TYPES__
+ #include <errno.h>
++#include <linux/elf.h>
+ #include <linux/futex.h>
+ #include <time.h>
+ #include <sys/time.h>
+@@ -1550,6 +1551,129 @@ void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
+ do_not_expect_pkey_fault("plain read on recently PROT_EXEC area");
+ }
+
++#if defined(__i386__) || defined(__x86_64__)
++void test_ptrace_modifies_pkru(int *ptr, u16 pkey)
++{
++ u32 new_pkru;
++ pid_t child;
++ int status, ret;
++ int pkey_offset = pkey_reg_xstate_offset();
++ size_t xsave_size = cpu_max_xsave_size();
++ void *xsave;
++ u32 *pkey_register;
++ u64 *xstate_bv;
++ struct iovec iov;
++
++ new_pkru = ~read_pkey_reg();
++ /* Don't make PROT_EXEC mappings inaccessible */
++ new_pkru &= ~3;
++
++ child = fork();
++ pkey_assert(child >= 0);
++ dprintf3("[%d] fork() ret: %d\n", getpid(), child);
++ if (!child) {
++ ptrace(PTRACE_TRACEME, 0, 0, 0);
++ /* Stop and allow the tracer to modify PKRU directly */
++ raise(SIGSTOP);
++
++ /*
++ * need __read_pkey_reg() version so we do not do shadow_pkey_reg
++ * checking
++ */
++ if (__read_pkey_reg() != new_pkru)
++ exit(1);
++
++ /* Stop and allow the tracer to clear XSTATE_BV for PKRU */
++ raise(SIGSTOP);
++
++ if (__read_pkey_reg() != 0)
++ exit(1);
++
++ /* Stop and allow the tracer to examine PKRU */
++ raise(SIGSTOP);
++
++ exit(0);
++ }
++
++ pkey_assert(child == waitpid(child, &status, 0));
++ dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
++ pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
++
++ xsave = (void *)malloc(xsave_size);
++ pkey_assert(xsave > 0);
++
++ /* Modify the PKRU register directly */
++ iov.iov_base = xsave;
++ iov.iov_len = xsave_size;
++ ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++
++ pkey_register = (u32 *)(xsave + pkey_offset);
++ pkey_assert(*pkey_register == read_pkey_reg());
++
++ *pkey_register = new_pkru;
++
++ ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++
++ /* Test that the modification is visible in ptrace before any execution */
++ memset(xsave, 0xCC, xsave_size);
++ ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++ pkey_assert(*pkey_register == new_pkru);
++
++ /* Execute the tracee */
++ ret = ptrace(PTRACE_CONT, child, 0, 0);
++ pkey_assert(ret == 0);
++
++ /* Test that the tracee saw the PKRU value change */
++ pkey_assert(child == waitpid(child, &status, 0));
++ dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
++ pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
++
++ /* Test that the modification is visible in ptrace after execution */
++ memset(xsave, 0xCC, xsave_size);
++ ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++ pkey_assert(*pkey_register == new_pkru);
++
++ /* Clear the PKRU bit from XSTATE_BV */
++ xstate_bv = (u64 *)(xsave + 512);
++ *xstate_bv &= ~(1 << 9);
++
++ ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++
++ /* Test that the modification is visible in ptrace before any execution */
++ memset(xsave, 0xCC, xsave_size);
++ ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++ pkey_assert(*pkey_register == 0);
++
++ ret = ptrace(PTRACE_CONT, child, 0, 0);
++ pkey_assert(ret == 0);
++
++ /* Test that the tracee saw the PKRU value go to 0 */
++ pkey_assert(child == waitpid(child, &status, 0));
++ dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
++ pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
++
++ /* Test that the modification is visible in ptrace after execution */
++ memset(xsave, 0xCC, xsave_size);
++ ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
++ pkey_assert(ret == 0);
++ pkey_assert(*pkey_register == 0);
++
++ ret = ptrace(PTRACE_CONT, child, 0, 0);
++ pkey_assert(ret == 0);
++ pkey_assert(child == waitpid(child, &status, 0));
++ dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
++ pkey_assert(WIFEXITED(status));
++ pkey_assert(WEXITSTATUS(status) == 0);
++ free(xsave);
++}
++#endif
++
+ void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
+ {
+ int size = PAGE_SIZE;
+@@ -1585,6 +1709,9 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
+ test_pkey_syscalls_bad_args,
+ test_pkey_alloc_exhaust,
+ test_pkey_alloc_free_attach_pkey0,
++#if defined(__i386__) || defined(__x86_64__)
++ test_ptrace_modifies_pkru,
++#endif
+ };
+
+ void run_tests_once(void)