diff options
-rw-r--r-- | 0000_README | 4 | ||||
-rw-r--r-- | 1087_linux-5.15.88.patch | 656 |
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) |