From 54cf700e35eb14ea256348e429febeb147d38390 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Jul 2019 13:07:02 +0200 Subject: [PATCH] 5.1-stable patches added patches: arm64-irqflags-add-condition-flags-to-inline-asm-clobber-list.patch arm64-tegra-fix-agic-register-range.patch fs-proc-proc_sysctl.c-fix-the-default-values-of-i_uid-i_gid-on-proc-sys-inodes.patch i3c-fix-i2c-and-i3c-scl-rate-by-bus-mode.patch signal-correct-namespace-fixups-of-si_pid-and-si_uid.patch signal-usb-replace-kill_pid_info_as_cred-with-kill_pid_usb_asyncio.patch --- ...ion-flags-to-inline-asm-clobber-list.patch | 46 ++ .../arm64-tegra-fix-agic-register-range.patch | 38 ++ ...es-of-i_uid-i_gid-on-proc-sys-inodes.patch | 87 ++++ ...fix-i2c-and-i3c-scl-rate-by-bus-mode.patch | 123 +++++ queue-5.1/series | 6 + ...amespace-fixups-of-si_pid-and-si_uid.patch | 142 ++++++ ...fo_as_cred-with-kill_pid_usb_asyncio.patch | 458 ++++++++++++++++++ 7 files changed, 900 insertions(+) create mode 100644 queue-5.1/arm64-irqflags-add-condition-flags-to-inline-asm-clobber-list.patch create mode 100644 queue-5.1/arm64-tegra-fix-agic-register-range.patch create mode 100644 queue-5.1/fs-proc-proc_sysctl.c-fix-the-default-values-of-i_uid-i_gid-on-proc-sys-inodes.patch create mode 100644 queue-5.1/i3c-fix-i2c-and-i3c-scl-rate-by-bus-mode.patch create mode 100644 queue-5.1/signal-correct-namespace-fixups-of-si_pid-and-si_uid.patch create mode 100644 queue-5.1/signal-usb-replace-kill_pid_info_as_cred-with-kill_pid_usb_asyncio.patch diff --git a/queue-5.1/arm64-irqflags-add-condition-flags-to-inline-asm-clobber-list.patch b/queue-5.1/arm64-irqflags-add-condition-flags-to-inline-asm-clobber-list.patch new file mode 100644 index 00000000000..c6b7c2fc40f --- /dev/null +++ b/queue-5.1/arm64-irqflags-add-condition-flags-to-inline-asm-clobber-list.patch @@ -0,0 +1,46 @@ +From f57065782f245ca96f1472209a485073bbc11247 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Tue, 11 Jun 2019 10:38:08 +0100 +Subject: arm64: irqflags: Add condition flags to inline asm clobber list + +From: Julien Thierry + +commit f57065782f245ca96f1472209a485073bbc11247 upstream. + +Some of the inline assembly instruction use the condition flags and need +to include "cc" in the clobber list. + +Fixes: 4a503217ce37 ("arm64: irqflags: Use ICC_PMR_EL1 for interrupt masking") +Cc: # 5.1.x- +Suggested-by: Marc Zyngier +Cc: Will Deacon +Reviewed-by: Marc Zyngier +Acked-by: Mark Rutland +Signed-off-by: Julien Thierry +Signed-off-by: Catalin Marinas +Signed-off-by: Greg Kroah-Hartman + +--- + arch/arm64/include/asm/irqflags.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm64/include/asm/irqflags.h ++++ b/arch/arm64/include/asm/irqflags.h +@@ -92,7 +92,7 @@ static inline unsigned long arch_local_s + ARM64_HAS_IRQ_PRIO_MASKING) + : "=&r" (flags), "+r" (daif_bits) + : "r" ((unsigned long) GIC_PRIO_IRQOFF) +- : "memory"); ++ : "cc", "memory"); + + return flags; + } +@@ -136,7 +136,7 @@ static inline int arch_irqs_disabled_fla + ARM64_HAS_IRQ_PRIO_MASKING) + : "=&r" (res) + : "r" ((int) flags) +- : "memory"); ++ : "cc", "memory"); + + return res; + } diff --git a/queue-5.1/arm64-tegra-fix-agic-register-range.patch b/queue-5.1/arm64-tegra-fix-agic-register-range.patch new file mode 100644 index 00000000000..6421d1035b3 --- /dev/null +++ b/queue-5.1/arm64-tegra-fix-agic-register-range.patch @@ -0,0 +1,38 @@ +From ba24eee6686f6ed3738602b54d959253316a9541 Mon Sep 17 00:00:00 2001 +From: Jon Hunter +Date: Thu, 20 Jun 2019 09:17:00 +0100 +Subject: arm64: tegra: Fix AGIC register range + +From: Jon Hunter + +commit ba24eee6686f6ed3738602b54d959253316a9541 upstream. + +The Tegra AGIC interrupt controller is an ARM GIC400 interrupt +controller. Per the ARM GIC device-tree binding, the first address +region is for the GIC distributor registers and the second address +region is for the GIC CPU interface registers. The address space for +the distributor registers is 4kB, but currently this is incorrectly +defined as 8kB for the Tegra AGIC and overlaps with the CPU interface +registers. Correct the address space for the distributor to be 4kB. + +Cc: stable@vger.kernel.org +Signed-off-by: Jon Hunter +Fixes: bcdbde433542 ("arm64: tegra: Add AGIC node for Tegra210") +Signed-off-by: Thierry Reding +Signed-off-by: Greg Kroah-Hartman + +--- + arch/arm64/boot/dts/nvidia/tegra210.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi ++++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi +@@ -1250,7 +1250,7 @@ + compatible = "nvidia,tegra210-agic"; + #interrupt-cells = <3>; + interrupt-controller; +- reg = <0x702f9000 0x2000>, ++ reg = <0x702f9000 0x1000>, + <0x702fa000 0x2000>; + interrupts = ; + clocks = <&tegra_car TEGRA210_CLK_APE>; diff --git a/queue-5.1/fs-proc-proc_sysctl.c-fix-the-default-values-of-i_uid-i_gid-on-proc-sys-inodes.patch b/queue-5.1/fs-proc-proc_sysctl.c-fix-the-default-values-of-i_uid-i_gid-on-proc-sys-inodes.patch new file mode 100644 index 00000000000..c0345a3de93 --- /dev/null +++ b/queue-5.1/fs-proc-proc_sysctl.c-fix-the-default-values-of-i_uid-i_gid-on-proc-sys-inodes.patch @@ -0,0 +1,87 @@ +From 5ec27ec735ba0477d48c80561cc5e856f0c5dfaf Mon Sep 17 00:00:00 2001 +From: Radoslaw Burny +Date: Tue, 16 Jul 2019 16:26:51 -0700 +Subject: fs/proc/proc_sysctl.c: fix the default values of i_uid/i_gid on /proc/sys inodes. + +From: Radoslaw Burny + +commit 5ec27ec735ba0477d48c80561cc5e856f0c5dfaf upstream. + +Normally, the inode's i_uid/i_gid are translated relative to s_user_ns, +but this is not a correct behavior for proc. Since sysctl permission +check in test_perm is done against GLOBAL_ROOT_[UG]ID, it makes more +sense to use these values in u_[ug]id of proc inodes. In other words: +although uid/gid in the inode is not read during test_perm, the inode +logically belongs to the root of the namespace. I have confirmed this +with Eric Biederman at LPC and in this thread: + https://lore.kernel.org/lkml/87k1kzjdff.fsf@xmission.com + +Consequences +============ + +Since the i_[ug]id values of proc nodes are not used for permissions +checks, this change usually makes no functional difference. However, it +causes an issue in a setup where: + + * a namespace container is created without root user in container - + hence the i_[ug]id of proc nodes are set to INVALID_[UG]ID + + * container creator tries to configure it by writing /proc/sys files, + e.g. writing /proc/sys/kernel/shmmax to configure shared memory limit + +Kernel does not allow to open an inode for writing if its i_[ug]id are +invalid, making it impossible to write shmmax and thus - configure the +container. + +Using a container with no root mapping is apparently rare, but we do use +this configuration at Google. Also, we use a generic tool to configure +the container limits, and the inability to write any of them causes a +failure. + +History +======= + +The invalid uids/gids in inodes first appeared due to 81754357770e (fs: +Update i_[ug]id_(read|write) to translate relative to s_user_ns). +However, AFAIK, this did not immediately cause any issues. The +inability to write to these "invalid" inodes was only caused by a later +commit 0bd23d09b874 (vfs: Don't modify inodes with a uid or gid unknown +to the vfs). + +Tested: Used a repro program that creates a user namespace without any +mapping and stat'ed /proc/$PID/root/proc/sys/kernel/shmmax from outside. +Before the change, it shows the overflow uid, with the change it's 0. +The overflow uid indicates that the uid in the inode is not correct and +thus it is not possible to open the file for writing. + +Link: http://lkml.kernel.org/r/20190708115130.250149-1-rburny@google.com +Fixes: 0bd23d09b874 ("vfs: Don't modify inodes with a uid or gid unknown to the vfs") +Signed-off-by: Radoslaw Burny +Acked-by: Luis Chamberlain +Cc: Kees Cook +Cc: "Eric W . Biederman" +Cc: Seth Forshee +Cc: John Sperbeck +Cc: Alexey Dobriyan +Cc: [4.8+] +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + fs/proc/proc_sysctl.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/fs/proc/proc_sysctl.c ++++ b/fs/proc/proc_sysctl.c +@@ -498,6 +498,10 @@ static struct inode *proc_sys_make_inode + + if (root->set_ownership) + root->set_ownership(head, table, &inode->i_uid, &inode->i_gid); ++ else { ++ inode->i_uid = GLOBAL_ROOT_UID; ++ inode->i_gid = GLOBAL_ROOT_GID; ++ } + + return inode; + } diff --git a/queue-5.1/i3c-fix-i2c-and-i3c-scl-rate-by-bus-mode.patch b/queue-5.1/i3c-fix-i2c-and-i3c-scl-rate-by-bus-mode.patch new file mode 100644 index 00000000000..7f5a31e3791 --- /dev/null +++ b/queue-5.1/i3c-fix-i2c-and-i3c-scl-rate-by-bus-mode.patch @@ -0,0 +1,123 @@ +From ecc8fb54bd443bf69996d9d5ddb8d90a50f14936 Mon Sep 17 00:00:00 2001 +From: Vitor Soares +Date: Wed, 19 Jun 2019 20:36:31 +0200 +Subject: i3c: fix i2c and i3c scl rate by bus mode + +From: Vitor Soares + +commit ecc8fb54bd443bf69996d9d5ddb8d90a50f14936 upstream. + +Currently the I3C framework limits SCL frequency to FM speed when +dealing with a mixed slow bus, even if all I2C devices are FM+ capable. + +The core was also not accounting for I3C speed limitations when +operating in mixed slow mode and was erroneously using FM+ speed as the +max I2C speed when operating in mixed fast mode. + +Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") +Signed-off-by: Vitor Soares +Cc: Boris Brezillon +Cc: +Cc: +Signed-off-by: Boris Brezillon +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/i3c/master.c | 51 ++++++++++++++++++++++++++++++++++++++------------- + 1 file changed, 38 insertions(+), 13 deletions(-) + +--- a/drivers/i3c/master.c ++++ b/drivers/i3c/master.c +@@ -91,6 +91,12 @@ void i3c_bus_normaluse_unlock(struct i3c + up_read(&bus->lock); + } + ++static struct i3c_master_controller * ++i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) ++{ ++ return container_of(i3cbus, struct i3c_master_controller, bus); ++} ++ + static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) + { + return container_of(dev, struct i3c_master_controller, dev); +@@ -565,20 +571,38 @@ static const struct device_type i3c_mast + .groups = i3c_masterdev_groups, + }; + +-int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode) ++int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, ++ unsigned long max_i2c_scl_rate) + { +- i3cbus->mode = mode; ++ struct i3c_master_controller *master = i3c_bus_to_i3c_master(i3cbus); + +- if (!i3cbus->scl_rate.i3c) +- i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; ++ i3cbus->mode = mode; + +- if (!i3cbus->scl_rate.i2c) { +- if (i3cbus->mode == I3C_BUS_MODE_MIXED_SLOW) +- i3cbus->scl_rate.i2c = I3C_BUS_I2C_FM_SCL_RATE; +- else +- i3cbus->scl_rate.i2c = I3C_BUS_I2C_FM_PLUS_SCL_RATE; ++ switch (i3cbus->mode) { ++ case I3C_BUS_MODE_PURE: ++ if (!i3cbus->scl_rate.i3c) ++ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; ++ break; ++ case I3C_BUS_MODE_MIXED_FAST: ++ if (!i3cbus->scl_rate.i3c) ++ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; ++ if (!i3cbus->scl_rate.i2c) ++ i3cbus->scl_rate.i2c = max_i2c_scl_rate; ++ break; ++ case I3C_BUS_MODE_MIXED_SLOW: ++ if (!i3cbus->scl_rate.i2c) ++ i3cbus->scl_rate.i2c = max_i2c_scl_rate; ++ if (!i3cbus->scl_rate.i3c || ++ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c) ++ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c; ++ break; ++ default: ++ return -EINVAL; + } + ++ dev_dbg(&master->dev, "i2c-scl = %ld Hz i3c-scl = %ld Hz\n", ++ i3cbus->scl_rate.i2c, i3cbus->scl_rate.i3c); ++ + /* + * I3C/I2C frequency may have been overridden, check that user-provided + * values are not exceeding max possible frequency. +@@ -1966,9 +1990,6 @@ of_i3c_master_add_i2c_boardinfo(struct i + /* LVR is encoded in reg[2]. */ + boardinfo->lvr = reg[2]; + +- if (boardinfo->lvr & I3C_LVR_I2C_FM_MODE) +- master->bus.scl_rate.i2c = I3C_BUS_I2C_FM_SCL_RATE; +- + list_add_tail(&boardinfo->node, &master->boardinfo.i2c); + of_node_get(node); + +@@ -2417,6 +2438,7 @@ int i3c_master_register(struct i3c_maste + const struct i3c_master_controller_ops *ops, + bool secondary) + { ++ unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_RATE; + struct i3c_bus *i3cbus = i3c_master_get_bus(master); + enum i3c_bus_mode mode = I3C_BUS_MODE_PURE; + struct i2c_dev_boardinfo *i2cbi; +@@ -2466,9 +2488,12 @@ int i3c_master_register(struct i3c_maste + ret = -EINVAL; + goto err_put_dev; + } ++ ++ if (i2cbi->lvr & I3C_LVR_I2C_FM_MODE) ++ i2c_scl_rate = I3C_BUS_I2C_FM_SCL_RATE; + } + +- ret = i3c_bus_set_mode(i3cbus, mode); ++ ret = i3c_bus_set_mode(i3cbus, mode, i2c_scl_rate); + if (ret) + goto err_put_dev; + diff --git a/queue-5.1/series b/queue-5.1/series index 815746a194b..2bc90b60098 100644 --- a/queue-5.1/series +++ b/queue-5.1/series @@ -303,3 +303,9 @@ kvm-ppc-book3s-hv-signed-extend-decrementer-value-if-not-using-large-decrementer kvm-ppc-book3s-hv-clear-pending-decrementer-exceptions-on-nested-guest-entry.patch kvm-ppc-book3s-hv-fix-cr0-setting-in-tm-emulation.patch kvm-x86-vpmu-refine-kvm_pmu-err-msg-when-event-creation-failed.patch +arm64-tegra-fix-agic-register-range.patch +arm64-irqflags-add-condition-flags-to-inline-asm-clobber-list.patch +signal-usb-replace-kill_pid_info_as_cred-with-kill_pid_usb_asyncio.patch +signal-correct-namespace-fixups-of-si_pid-and-si_uid.patch +fs-proc-proc_sysctl.c-fix-the-default-values-of-i_uid-i_gid-on-proc-sys-inodes.patch +i3c-fix-i2c-and-i3c-scl-rate-by-bus-mode.patch diff --git a/queue-5.1/signal-correct-namespace-fixups-of-si_pid-and-si_uid.patch b/queue-5.1/signal-correct-namespace-fixups-of-si_pid-and-si_uid.patch new file mode 100644 index 00000000000..0436d489342 --- /dev/null +++ b/queue-5.1/signal-correct-namespace-fixups-of-si_pid-and-si_uid.patch @@ -0,0 +1,142 @@ +From 7a0cf094944e2540758b7f957eb6846d5126f535 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Wed, 15 May 2019 22:54:56 -0500 +Subject: signal: Correct namespace fixups of si_pid and si_uid + +From: Eric W. Biederman + +commit 7a0cf094944e2540758b7f957eb6846d5126f535 upstream. + +The function send_signal was split from __send_signal so that it would +be possible to bypass the namespace logic based upon current[1]. As it +turns out the si_pid and the si_uid fixup are both inappropriate in +the case of kill_pid_usb_asyncio so move that logic into send_signal. + +It is difficult to arrange but possible for a signal with an si_code +of SI_TIMER or SI_SIGIO to be sent across namespace boundaries. In +which case tests for when it is ok to change si_pid and si_uid based +on SI_FROMUSER are incorrect. Replace the use of SI_FROMUSER with a +new test has_si_pid_and_used based on siginfo_layout. + +Now that the uid fixup is no longer present after expanding +SEND_SIG_NOINFO properly calculate the si_uid that the target +task needs to read. + +[1] 7978b567d315 ("signals: add from_ancestor_ns parameter to send_signal()") +Cc: stable@vger.kernel.org +Fixes: 6588c1e3ff01 ("signals: SI_USER: Masquerade si_pid when crossing pid ns boundary") +Fixes: 6b550f949594 ("user namespace: make signal.c respect user namespaces") +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/signal.c | 67 +++++++++++++++++++++++++++++++++----------------------- + 1 file changed, 40 insertions(+), 27 deletions(-) + +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -1053,27 +1053,6 @@ static inline bool legacy_queue(struct s + return (sig < SIGRTMIN) && sigismember(&signals->signal, sig); + } + +-#ifdef CONFIG_USER_NS +-static inline void userns_fixup_signal_uid(struct kernel_siginfo *info, struct task_struct *t) +-{ +- if (current_user_ns() == task_cred_xxx(t, user_ns)) +- return; +- +- if (SI_FROMKERNEL(info)) +- return; +- +- rcu_read_lock(); +- info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns), +- make_kuid(current_user_ns(), info->si_uid)); +- rcu_read_unlock(); +-} +-#else +-static inline void userns_fixup_signal_uid(struct kernel_siginfo *info, struct task_struct *t) +-{ +- return; +-} +-#endif +- + static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struct *t, + enum pid_type type, int from_ancestor_ns) + { +@@ -1131,7 +1110,11 @@ static int __send_signal(int sig, struct + q->info.si_code = SI_USER; + q->info.si_pid = task_tgid_nr_ns(current, + task_active_pid_ns(t)); +- q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); ++ rcu_read_lock(); ++ q->info.si_uid = ++ from_kuid_munged(task_cred_xxx(t, user_ns), ++ current_uid()); ++ rcu_read_unlock(); + break; + case (unsigned long) SEND_SIG_PRIV: + clear_siginfo(&q->info); +@@ -1143,13 +1126,8 @@ static int __send_signal(int sig, struct + break; + default: + copy_siginfo(&q->info, info); +- if (from_ancestor_ns) +- q->info.si_pid = 0; + break; + } +- +- userns_fixup_signal_uid(&q->info, t); +- + } else if (!is_si_special(info)) { + if (sig >= SIGRTMIN && info->si_code != SI_USER) { + /* +@@ -1193,6 +1171,28 @@ ret: + return ret; + } + ++static inline bool has_si_pid_and_uid(struct kernel_siginfo *info) ++{ ++ bool ret = false; ++ switch (siginfo_layout(info->si_signo, info->si_code)) { ++ case SIL_KILL: ++ case SIL_CHLD: ++ case SIL_RT: ++ ret = true; ++ break; ++ case SIL_TIMER: ++ case SIL_POLL: ++ case SIL_FAULT: ++ case SIL_FAULT_MCEERR: ++ case SIL_FAULT_BNDERR: ++ case SIL_FAULT_PKUERR: ++ case SIL_SYS: ++ ret = false; ++ break; ++ } ++ return ret; ++} ++ + static int send_signal(int sig, struct kernel_siginfo *info, struct task_struct *t, + enum pid_type type) + { +@@ -1202,7 +1202,20 @@ static int send_signal(int sig, struct k + from_ancestor_ns = si_fromuser(info) && + !task_pid_nr_ns(current, task_active_pid_ns(t)); + #endif ++ if (!is_si_special(info) && has_si_pid_and_uid(info)) { ++ struct user_namespace *t_user_ns; ++ ++ rcu_read_lock(); ++ t_user_ns = task_cred_xxx(t, user_ns); ++ if (current_user_ns() != t_user_ns) { ++ kuid_t uid = make_kuid(current_user_ns(), info->si_uid); ++ info->si_uid = from_kuid_munged(t_user_ns, uid); ++ } ++ rcu_read_unlock(); + ++ if (!task_pid_nr_ns(current, task_active_pid_ns(t))) ++ info->si_pid = 0; ++ } + return __send_signal(sig, info, t, type, from_ancestor_ns); + } + diff --git a/queue-5.1/signal-usb-replace-kill_pid_info_as_cred-with-kill_pid_usb_asyncio.patch b/queue-5.1/signal-usb-replace-kill_pid_info_as_cred-with-kill_pid_usb_asyncio.patch new file mode 100644 index 00000000000..8ff9f9c9ddc --- /dev/null +++ b/queue-5.1/signal-usb-replace-kill_pid_info_as_cred-with-kill_pid_usb_asyncio.patch @@ -0,0 +1,458 @@ +From 70f1b0d34bdf03065fe869e93cc17cad1ea20c4a Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Thu, 7 Feb 2019 19:44:12 -0600 +Subject: signal/usb: Replace kill_pid_info_as_cred with kill_pid_usb_asyncio + +From: Eric W. Biederman + +commit 70f1b0d34bdf03065fe869e93cc17cad1ea20c4a upstream. + +The usb support for asyncio encoded one of it's values in the wrong +field. It should have used si_value but instead used si_addr which is +not present in the _rt union member of struct siginfo. + +The practical result of this is that on a 64bit big endian kernel +when delivering a signal to a 32bit process the si_addr field +is set to NULL, instead of the expected pointer value. + +This issue can not be fixed in copy_siginfo_to_user32 as the usb +usage of the the _sigfault (aka si_addr) member of the siginfo +union when SI_ASYNCIO is set is incompatible with the POSIX and +glibc usage of the _rt member of the siginfo union. + +Therefore replace kill_pid_info_as_cred with kill_pid_usb_asyncio a +dedicated function for this one specific case. There are no other +users of kill_pid_info_as_cred so this specialization should have no +impact on the amount of code in the kernel. Have kill_pid_usb_asyncio +take instead of a siginfo_t which is difficult and error prone, 3 +arguments, a signal number, an errno value, and an address enconded as +a sigval_t. The encoding of the address as a sigval_t allows the +code that reads the userspace request for a signal to handle this +compat issue along with all of the other compat issues. + +Add BUILD_BUG_ONs in kernel/signal.c to ensure that we can now place +the pointer value at the in si_pid (instead of si_addr). That is the +code now verifies that si_pid and si_addr always occur at the same +location. Further the code veries that for native structures a value +placed in si_pid and spilling into si_uid will appear in userspace in +si_addr (on a byte by byte copy of siginfo or a field by field copy of +siginfo). The code also verifies that for a 64bit kernel and a 32bit +userspace the 32bit pointer will fit in si_pid. + +I have used the usbsig.c program below written by Alan Stern and +slightly tweaked by me to run on a big endian machine to verify the +issue exists (on sparc64) and to confirm the patch below fixes the issue. + + /* usbsig.c -- test USB async signal delivery */ + + #define _GNU_SOURCE + #include + #include + #include + #include + #include + #include + #include + #include + #include + + static struct usbdevfs_urb urb; + static struct usbdevfs_disconnectsignal ds; + static volatile sig_atomic_t done = 0; + + void urb_handler(int sig, siginfo_t *info , void *ucontext) + { + printf("Got signal %d, signo %d errno %d code %d addr: %p urb: %p\n", + sig, info->si_signo, info->si_errno, info->si_code, + info->si_addr, &urb); + + printf("%s\n", (info->si_addr == &urb) ? "Good" : "Bad"); + } + + void ds_handler(int sig, siginfo_t *info , void *ucontext) + { + printf("Got signal %d, signo %d errno %d code %d addr: %p ds: %p\n", + sig, info->si_signo, info->si_errno, info->si_code, + info->si_addr, &ds); + + printf("%s\n", (info->si_addr == &ds) ? "Good" : "Bad"); + done = 1; + } + + int main(int argc, char **argv) + { + char *devfilename; + int fd; + int rc; + struct sigaction act; + struct usb_ctrlrequest *req; + void *ptr; + char buf[80]; + + if (argc != 2) { + fprintf(stderr, "Usage: usbsig device-file-name\n"); + return 1; + } + + devfilename = argv[1]; + fd = open(devfilename, O_RDWR); + if (fd == -1) { + perror("Error opening device file"); + return 1; + } + + act.sa_sigaction = urb_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + + rc = sigaction(SIGUSR1, &act, NULL); + if (rc == -1) { + perror("Error in sigaction"); + return 1; + } + + act.sa_sigaction = ds_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + + rc = sigaction(SIGUSR2, &act, NULL); + if (rc == -1) { + perror("Error in sigaction"); + return 1; + } + + memset(&urb, 0, sizeof(urb)); + urb.type = USBDEVFS_URB_TYPE_CONTROL; + urb.endpoint = USB_DIR_IN | 0; + urb.buffer = buf; + urb.buffer_length = sizeof(buf); + urb.signr = SIGUSR1; + + req = (struct usb_ctrlrequest *) buf; + req->bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req->bRequest = USB_REQ_GET_DESCRIPTOR; + req->wValue = htole16(USB_DT_DEVICE << 8); + req->wIndex = htole16(0); + req->wLength = htole16(sizeof(buf) - sizeof(*req)); + + rc = ioctl(fd, USBDEVFS_SUBMITURB, &urb); + if (rc == -1) { + perror("Error in SUBMITURB ioctl"); + return 1; + } + + rc = ioctl(fd, USBDEVFS_REAPURB, &ptr); + if (rc == -1) { + perror("Error in REAPURB ioctl"); + return 1; + } + + memset(&ds, 0, sizeof(ds)); + ds.signr = SIGUSR2; + ds.context = &ds; + rc = ioctl(fd, USBDEVFS_DISCSIGNAL, &ds); + if (rc == -1) { + perror("Error in DISCSIGNAL ioctl"); + return 1; + } + + printf("Waiting for usb disconnect\n"); + while (!done) { + sleep(1); + } + + close(fd); + return 0; + } + +Cc: Greg Kroah-Hartman +Cc: linux-usb@vger.kernel.org +Cc: Alan Stern +Cc: Oliver Neukum +Fixes: v2.3.39 +Cc: stable@vger.kernel.org +Acked-by: Alan Stern +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/core/devio.c | 48 ++++++++++++++--------------- + include/linux/sched/signal.h | 2 - + kernel/signal.c | 69 ++++++++++++++++++++++++++++++++++++++----- + 3 files changed, 86 insertions(+), 33 deletions(-) + +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -63,7 +63,7 @@ struct usb_dev_state { + unsigned int discsignr; + struct pid *disc_pid; + const struct cred *cred; +- void __user *disccontext; ++ sigval_t disccontext; + unsigned long ifclaimed; + u32 disabled_bulk_eps; + bool privileges_dropped; +@@ -90,6 +90,7 @@ struct async { + unsigned int ifnum; + void __user *userbuffer; + void __user *userurb; ++ sigval_t userurb_sigval; + struct urb *urb; + struct usb_memory *usbm; + unsigned int mem_usage; +@@ -582,22 +583,19 @@ static void async_completed(struct urb * + { + struct async *as = urb->context; + struct usb_dev_state *ps = as->ps; +- struct kernel_siginfo sinfo; + struct pid *pid = NULL; + const struct cred *cred = NULL; + unsigned long flags; +- int signr; ++ sigval_t addr; ++ int signr, errno; + + spin_lock_irqsave(&ps->lock, flags); + list_move_tail(&as->asynclist, &ps->async_completed); + as->status = urb->status; + signr = as->signr; + if (signr) { +- clear_siginfo(&sinfo); +- sinfo.si_signo = as->signr; +- sinfo.si_errno = as->status; +- sinfo.si_code = SI_ASYNCIO; +- sinfo.si_addr = as->userurb; ++ errno = as->status; ++ addr = as->userurb_sigval; + pid = get_pid(as->pid); + cred = get_cred(as->cred); + } +@@ -615,7 +613,7 @@ static void async_completed(struct urb * + spin_unlock_irqrestore(&ps->lock, flags); + + if (signr) { +- kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred); ++ kill_pid_usb_asyncio(signr, errno, addr, pid, cred); + put_pid(pid); + put_cred(cred); + } +@@ -1427,7 +1425,7 @@ find_memory_area(struct usb_dev_state *p + + static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb, + struct usbdevfs_iso_packet_desc __user *iso_frame_desc, +- void __user *arg) ++ void __user *arg, sigval_t userurb_sigval) + { + struct usbdevfs_iso_packet_desc *isopkt = NULL; + struct usb_host_endpoint *ep; +@@ -1727,6 +1725,7 @@ static int proc_do_submiturb(struct usb_ + isopkt = NULL; + as->ps = ps; + as->userurb = arg; ++ as->userurb_sigval = userurb_sigval; + if (as->usbm) { + unsigned long uurb_start = (unsigned long)uurb->buffer; + +@@ -1801,13 +1800,17 @@ static int proc_do_submiturb(struct usb_ + static int proc_submiturb(struct usb_dev_state *ps, void __user *arg) + { + struct usbdevfs_urb uurb; ++ sigval_t userurb_sigval; + + if (copy_from_user(&uurb, arg, sizeof(uurb))) + return -EFAULT; + ++ memset(&userurb_sigval, 0, sizeof(userurb_sigval)); ++ userurb_sigval.sival_ptr = arg; ++ + return proc_do_submiturb(ps, &uurb, + (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), +- arg); ++ arg, userurb_sigval); + } + + static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg) +@@ -1977,7 +1980,7 @@ static int proc_disconnectsignal_compat( + if (copy_from_user(&ds, arg, sizeof(ds))) + return -EFAULT; + ps->discsignr = ds.signr; +- ps->disccontext = compat_ptr(ds.context); ++ ps->disccontext.sival_int = ds.context; + return 0; + } + +@@ -2005,13 +2008,17 @@ static int get_urb32(struct usbdevfs_urb + static int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg) + { + struct usbdevfs_urb uurb; ++ sigval_t userurb_sigval; + + if (get_urb32(&uurb, (struct usbdevfs_urb32 __user *)arg)) + return -EFAULT; + ++ memset(&userurb_sigval, 0, sizeof(userurb_sigval)); ++ userurb_sigval.sival_int = ptr_to_compat(arg); ++ + return proc_do_submiturb(ps, &uurb, + ((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc, +- arg); ++ arg, userurb_sigval); + } + + static int processcompl_compat(struct async *as, void __user * __user *arg) +@@ -2092,7 +2099,7 @@ static int proc_disconnectsignal(struct + if (copy_from_user(&ds, arg, sizeof(ds))) + return -EFAULT; + ps->discsignr = ds.signr; +- ps->disccontext = ds.context; ++ ps->disccontext.sival_ptr = ds.context; + return 0; + } + +@@ -2614,22 +2621,15 @@ const struct file_operations usbdev_file + static void usbdev_remove(struct usb_device *udev) + { + struct usb_dev_state *ps; +- struct kernel_siginfo sinfo; + + while (!list_empty(&udev->filelist)) { + ps = list_entry(udev->filelist.next, struct usb_dev_state, list); + destroy_all_async(ps); + wake_up_all(&ps->wait); + list_del_init(&ps->list); +- if (ps->discsignr) { +- clear_siginfo(&sinfo); +- sinfo.si_signo = ps->discsignr; +- sinfo.si_errno = EPIPE; +- sinfo.si_code = SI_ASYNCIO; +- sinfo.si_addr = ps->disccontext; +- kill_pid_info_as_cred(ps->discsignr, &sinfo, +- ps->disc_pid, ps->cred); +- } ++ if (ps->discsignr) ++ kill_pid_usb_asyncio(ps->discsignr, EPIPE, ps->disccontext, ++ ps->disc_pid, ps->cred); + } + } + +--- a/include/linux/sched/signal.h ++++ b/include/linux/sched/signal.h +@@ -328,7 +328,7 @@ extern void force_sigsegv(int sig, struc + extern int force_sig_info(int, struct kernel_siginfo *, struct task_struct *); + extern int __kill_pgrp_info(int sig, struct kernel_siginfo *info, struct pid *pgrp); + extern int kill_pid_info(int sig, struct kernel_siginfo *info, struct pid *pid); +-extern int kill_pid_info_as_cred(int, struct kernel_siginfo *, struct pid *, ++extern int kill_pid_usb_asyncio(int sig, int errno, sigval_t addr, struct pid *, + const struct cred *); + extern int kill_pgrp(struct pid *pid, int sig, int priv); + extern int kill_pid(struct pid *pid, int sig, int priv); +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -1436,13 +1436,44 @@ static inline bool kill_as_cred_perm(con + uid_eq(cred->uid, pcred->uid); + } + +-/* like kill_pid_info(), but doesn't use uid/euid of "current" */ +-int kill_pid_info_as_cred(int sig, struct kernel_siginfo *info, struct pid *pid, +- const struct cred *cred) ++/* ++ * The usb asyncio usage of siginfo is wrong. The glibc support ++ * for asyncio which uses SI_ASYNCIO assumes the layout is SIL_RT. ++ * AKA after the generic fields: ++ * kernel_pid_t si_pid; ++ * kernel_uid32_t si_uid; ++ * sigval_t si_value; ++ * ++ * Unfortunately when usb generates SI_ASYNCIO it assumes the layout ++ * after the generic fields is: ++ * void __user *si_addr; ++ * ++ * This is a practical problem when there is a 64bit big endian kernel ++ * and a 32bit userspace. As the 32bit address will encoded in the low ++ * 32bits of the pointer. Those low 32bits will be stored at higher ++ * address than appear in a 32 bit pointer. So userspace will not ++ * see the address it was expecting for it's completions. ++ * ++ * There is nothing in the encoding that can allow ++ * copy_siginfo_to_user32 to detect this confusion of formats, so ++ * handle this by requiring the caller of kill_pid_usb_asyncio to ++ * notice when this situration takes place and to store the 32bit ++ * pointer in sival_int, instead of sival_addr of the sigval_t addr ++ * parameter. ++ */ ++int kill_pid_usb_asyncio(int sig, int errno, sigval_t addr, ++ struct pid *pid, const struct cred *cred) + { +- int ret = -EINVAL; ++ struct kernel_siginfo info; + struct task_struct *p; + unsigned long flags; ++ int ret = -EINVAL; ++ ++ clear_siginfo(&info); ++ info.si_signo = sig; ++ info.si_errno = errno; ++ info.si_code = SI_ASYNCIO; ++ *((sigval_t *)&info.si_pid) = addr; + + if (!valid_signal(sig)) + return ret; +@@ -1453,17 +1484,17 @@ int kill_pid_info_as_cred(int sig, struc + ret = -ESRCH; + goto out_unlock; + } +- if (si_fromuser(info) && !kill_as_cred_perm(cred, p)) { ++ if (!kill_as_cred_perm(cred, p)) { + ret = -EPERM; + goto out_unlock; + } +- ret = security_task_kill(p, info, sig, cred); ++ ret = security_task_kill(p, &info, sig, cred); + if (ret) + goto out_unlock; + + if (sig) { + if (lock_task_sighand(p, &flags)) { +- ret = __send_signal(sig, info, p, PIDTYPE_TGID, 0); ++ ret = __send_signal(sig, &info, p, PIDTYPE_TGID, 0); + unlock_task_sighand(p, &flags); + } else + ret = -ESRCH; +@@ -1472,7 +1503,7 @@ out_unlock: + rcu_read_unlock(); + return ret; + } +-EXPORT_SYMBOL_GPL(kill_pid_info_as_cred); ++EXPORT_SYMBOL_GPL(kill_pid_usb_asyncio); + + /* + * kill_something_info() interprets pid in interesting ways just like kill(2). +@@ -4411,6 +4442,28 @@ static inline void siginfo_buildtime_che + CHECK_OFFSET(si_syscall); + CHECK_OFFSET(si_arch); + #undef CHECK_OFFSET ++ ++ /* usb asyncio */ ++ BUILD_BUG_ON(offsetof(struct siginfo, si_pid) != ++ offsetof(struct siginfo, si_addr)); ++ if (sizeof(int) == sizeof(void __user *)) { ++ BUILD_BUG_ON(sizeof_field(struct siginfo, si_pid) != ++ sizeof(void __user *)); ++ } else { ++ BUILD_BUG_ON((sizeof_field(struct siginfo, si_pid) + ++ sizeof_field(struct siginfo, si_uid)) != ++ sizeof(void __user *)); ++ BUILD_BUG_ON(offsetofend(struct siginfo, si_pid) != ++ offsetof(struct siginfo, si_uid)); ++ } ++#ifdef CONFIG_COMPAT ++ BUILD_BUG_ON(offsetof(struct compat_siginfo, si_pid) != ++ offsetof(struct compat_siginfo, si_addr)); ++ BUILD_BUG_ON(sizeof_field(struct compat_siginfo, si_pid) != ++ sizeof(compat_uptr_t)); ++ BUILD_BUG_ON(sizeof_field(struct compat_siginfo, si_pid) != ++ sizeof_field(struct siginfo, si_pid)); ++#endif + } + + void __init signals_init(void) -- 2.47.3