mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-18 22:06:49 +07:00
f4dce1ffd2
Since commit4c21b8fd8f
("MIPS: seccomp: Handle indirect system calls (o32)"), syscall_get_arguments() attempts to handle o32 indirect syscall arguments by incrementing both the start argument number and the number of arguments to fetch. However only the start argument number needs to be incremented. The number of arguments does not change, they're just shifted up by one, and in fact the output array is provided by the caller and is likely only n entries long, so reading more arguments overflows the output buffer. In the case of seccomp, this results in it fetching 7 arguments starting at the 2nd one, which overflows the unsigned long args[6] in populate_seccomp_data(). This clobbers the $s0 register from syscall_trace_enter() which __seccomp_phase1_filter() saved onto the stack, into which syscall_trace_enter() had placed its syscall number argument. This caused Chromium to crash. Credit goes to Milko for tracking it down as far as $s0 being clobbered. Fixes:4c21b8fd8f
("MIPS: seccomp: Handle indirect system calls (o32)") Reported-by: Milko Leporis <milko.leporis@imgtec.com> Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: linux-mips@linux-mips.org Cc: <stable@vger.kernel.org> # 3.15- Patchwork: https://patchwork.linux-mips.org/patch/12213/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
140 lines
3.0 KiB
C
140 lines
3.0 KiB
C
/*
|
|
* Access to user system call parameters and results
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* See asm-generic/syscall.h for descriptions of what we must do here.
|
|
*
|
|
* Copyright (C) 2012 Ralf Baechle <ralf@linux-mips.org>
|
|
*/
|
|
|
|
#ifndef __ASM_MIPS_SYSCALL_H
|
|
#define __ASM_MIPS_SYSCALL_H
|
|
|
|
#include <linux/compiler.h>
|
|
#include <uapi/linux/audit.h>
|
|
#include <linux/elf-em.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/unistd.h>
|
|
|
|
#ifndef __NR_syscall /* Only defined if _MIPS_SIM == _MIPS_SIM_ABI32 */
|
|
#define __NR_syscall 4000
|
|
#endif
|
|
|
|
static inline long syscall_get_nr(struct task_struct *task,
|
|
struct pt_regs *regs)
|
|
{
|
|
return current_thread_info()->syscall;
|
|
}
|
|
|
|
static inline unsigned long mips_get_syscall_arg(unsigned long *arg,
|
|
struct task_struct *task, struct pt_regs *regs, unsigned int n)
|
|
{
|
|
unsigned long usp __maybe_unused = regs->regs[29];
|
|
|
|
switch (n) {
|
|
case 0: case 1: case 2: case 3:
|
|
*arg = regs->regs[4 + n];
|
|
|
|
return 0;
|
|
|
|
#ifdef CONFIG_32BIT
|
|
case 4: case 5: case 6: case 7:
|
|
return get_user(*arg, (int *)usp + n);
|
|
#endif
|
|
|
|
#ifdef CONFIG_64BIT
|
|
case 4: case 5: case 6: case 7:
|
|
#ifdef CONFIG_MIPS32_O32
|
|
if (test_thread_flag(TIF_32BIT_REGS))
|
|
return get_user(*arg, (int *)usp + n);
|
|
else
|
|
#endif
|
|
*arg = regs->regs[4 + n];
|
|
|
|
return 0;
|
|
#endif
|
|
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
unreachable();
|
|
}
|
|
|
|
static inline long syscall_get_return_value(struct task_struct *task,
|
|
struct pt_regs *regs)
|
|
{
|
|
return regs->regs[2];
|
|
}
|
|
|
|
static inline void syscall_rollback(struct task_struct *task,
|
|
struct pt_regs *regs)
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
|
|
static inline void syscall_set_return_value(struct task_struct *task,
|
|
struct pt_regs *regs,
|
|
int error, long val)
|
|
{
|
|
if (error) {
|
|
regs->regs[2] = -error;
|
|
regs->regs[7] = -1;
|
|
} else {
|
|
regs->regs[2] = val;
|
|
regs->regs[7] = 0;
|
|
}
|
|
}
|
|
|
|
static inline void syscall_get_arguments(struct task_struct *task,
|
|
struct pt_regs *regs,
|
|
unsigned int i, unsigned int n,
|
|
unsigned long *args)
|
|
{
|
|
int ret;
|
|
/* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */
|
|
if ((config_enabled(CONFIG_32BIT) ||
|
|
test_tsk_thread_flag(task, TIF_32BIT_REGS)) &&
|
|
(regs->regs[2] == __NR_syscall))
|
|
i++;
|
|
|
|
while (n--)
|
|
ret |= mips_get_syscall_arg(args++, task, regs, i++);
|
|
|
|
/*
|
|
* No way to communicate an error because this is a void function.
|
|
*/
|
|
#if 0
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
extern const unsigned long sys_call_table[];
|
|
extern const unsigned long sys32_call_table[];
|
|
extern const unsigned long sysn32_call_table[];
|
|
|
|
static inline int syscall_get_arch(void)
|
|
{
|
|
int arch = AUDIT_ARCH_MIPS;
|
|
#ifdef CONFIG_64BIT
|
|
if (!test_thread_flag(TIF_32BIT_REGS)) {
|
|
arch |= __AUDIT_ARCH_64BIT;
|
|
/* N32 sets only TIF_32BIT_ADDR */
|
|
if (test_thread_flag(TIF_32BIT_ADDR))
|
|
arch |= __AUDIT_ARCH_CONVENTION_MIPS64_N32;
|
|
}
|
|
#endif
|
|
#if defined(__LITTLE_ENDIAN)
|
|
arch |= __AUDIT_ARCH_LE;
|
|
#endif
|
|
return arch;
|
|
}
|
|
|
|
#endif /* __ASM_MIPS_SYSCALL_H */
|