4912 lines
145 KiB
C
4912 lines
145 KiB
C
/*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
* Copyright (c) 2023 MCST
|
|
*/
|
|
|
|
/*
|
|
* This is implementation of system call handlers for E2K protected mode:
|
|
* int protected_sys_<syscallname>(const long a1, ... a6,
|
|
* const struct pt_regs *regs);
|
|
*/
|
|
|
|
#include <linux/syscalls.h>
|
|
#include <asm/compat.h>
|
|
#include <asm/e2k_debug.h>
|
|
|
|
#include <asm/mman.h>
|
|
#include <asm/convert_array.h>
|
|
#include <asm/prot_loader.h>
|
|
#include <asm/convert_array.h>
|
|
#include <asm/syscalls.h>
|
|
#include <asm/shmbuf.h>
|
|
#include <asm/prot_compat.h>
|
|
|
|
#include <linux/fdtable.h>
|
|
#include <linux/filter.h>
|
|
#include <linux/futex.h>
|
|
#include <linux/net.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/keyctl.h>
|
|
#include <linux/prctl.h>
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/msg.h>
|
|
#include <linux/mqueue.h>
|
|
#include <uapi/linux/io_uring.h>
|
|
#include <uapi/linux/sched/types.h>
|
|
#include <linux/kexec.h>
|
|
#include "linux/version.h"
|
|
|
|
#ifdef CONFIG_PROTECTED_MODE
|
|
|
|
#include <asm/protected_syscalls.h>
|
|
#include "protected_error_messages.in"
|
|
|
|
void pm_deliver_exception(int signo, int code, int errno)
|
|
/* Sometimes we need to deliver exception to end up execution of the current thread: */
|
|
{
|
|
struct kernel_siginfo info;
|
|
int ret;
|
|
|
|
if (signo == SIGABRT) {
|
|
PROTECTED_MODE_MESSAGE(0, PMSCERRMSG_EXECUTION_TERMINATED,
|
|
current->pid, current->comm, EINVAL);
|
|
force_sig(SIGABRT);
|
|
return;
|
|
}
|
|
|
|
/* Deliver exception: */
|
|
|
|
clear_siginfo(&info);
|
|
info.si_signo = signo; /* f.e. SIGILL */
|
|
info.si_code = code; /* f.e. ILL_ILLOPN - "illegal operand" */
|
|
info.si_errno = errno; /* f.e. -EINVAL */
|
|
|
|
ret = force_sig_info(&info);
|
|
if (ret)
|
|
pr_alert("%s:%d : force_sig_info(signo=%d) failed with error %d\n",
|
|
__FILE__, __LINE__, signo, ret);
|
|
}
|
|
|
|
void pm_deliver_sig_bnderr(const int arg_num,
|
|
const struct pt_regs *regs)
|
|
{
|
|
e2k_ptr_t ptr;
|
|
|
|
PROTECTED_MODE_MESSAGE(0, PMSCERRMSG_EXECUTION_TERMINATED,
|
|
current->pid, current->comm, EINVAL);
|
|
|
|
ptr.lo = regs->args[arg_num * 2 - 1];
|
|
ptr.hi = regs->args[arg_num * 2];
|
|
force_sig_bnderr((void __user *)(ptr.base + ptr.curptr),
|
|
(void __user *)ptr.base,
|
|
(void __user *)ptr.base + ptr.size);
|
|
}
|
|
|
|
|
|
|
|
static void __user *get_user_space(unsigned long len)
|
|
{
|
|
void __user *uspace;
|
|
long ret;
|
|
|
|
uspace = __get_user_space(len);
|
|
if (!uspace) {
|
|
pr_alert("%s:%d : %s() failed to allocate %lu bytes of user stack\n",
|
|
__FILE__, __LINE__, __func__, len);
|
|
pm_deliver_exception(SIGABRT, SI_KERNEL, ENOMEM);
|
|
}
|
|
ret = clear_user(uspace, len);
|
|
if (ret) {
|
|
pr_alert("%s() failed to clear %lu bytes at %s:%d\n",
|
|
__func__, ret, __FILE__, __LINE__);
|
|
pm_deliver_exception(SIGABRT, SI_KERNEL, EFAULT);
|
|
}
|
|
|
|
return uspace;
|
|
}
|
|
|
|
/*
|
|
* Counts the number of descriptors in array, which is terminated by NULL
|
|
* (For counting of elements in argv and envp arrays)
|
|
*/
|
|
notrace __section(".entry.text")
|
|
static int count_descriptors(long __user *prot_array, const int prot_array_size)
|
|
{
|
|
int i;
|
|
long tmp[1];
|
|
|
|
if (prot_array == NULL)
|
|
return 0;
|
|
|
|
/* Ensure that protected array is aligned and sized properly */
|
|
if (!IS_ALIGNED((u64) prot_array, 16))
|
|
return -EINVAL;
|
|
|
|
/* Read each entry */
|
|
for (i = 0; 8 * i + 16 <= prot_array_size; i += 2) {
|
|
long lo;
|
|
int ltag;
|
|
|
|
if (copy_from_user_with_tags(tmp, &prot_array[i], 16))
|
|
return -EFAULT;
|
|
|
|
NATIVE_LOAD_VAL_AND_TAGD(tmp, lo, ltag);
|
|
|
|
/* If zero is met, it is the end of array.
|
|
* NB> Rear user makes difference between zero and NULL.
|
|
* It may happen input array end up with zero, not NULL.
|
|
* Therefore we're looking for the first 0x0L in the array.
|
|
*/
|
|
if (lo == 0 && ltag == 0)
|
|
return i >> 1;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Scans environment for the given env var.
|
|
* Reports: value of the given env var; 0 - if doesn't exist.
|
|
*/
|
|
static inline
|
|
char *pm_getenv(const char *env_var_name, const size_t max_len)
|
|
{
|
|
/* NB> Length of the environment record expected less that 'max_len'. */
|
|
unsigned long __user uenvp;
|
|
size_t len, lenvar;
|
|
unsigned long kenvp;
|
|
unsigned long lmax = 128;
|
|
long copied;
|
|
|
|
if (!current->mm || !current->mm->env_start)
|
|
return 0;
|
|
if (current->mm->env_start >= current->mm->env_end)
|
|
return 0;
|
|
lenvar = strlen(env_var_name);
|
|
kenvp = (unsigned long)kmalloc(lmax, GFP_KERNEL);
|
|
for (uenvp = current->mm->env_start;
|
|
uenvp < current->mm->env_end;
|
|
uenvp += len) /* strnlen_user accounts terminating '\0' */ {
|
|
len = strnlen_user((void __user *)uenvp,
|
|
current->mm->env_end - uenvp);
|
|
if (!len)
|
|
break;
|
|
else if ((len < lenvar) || (len > max_len))
|
|
continue;
|
|
if (lmax < len) {
|
|
lmax = (len + 127) & 0xffffff80;
|
|
kenvp = (unsigned long)krealloc((void *)kenvp,
|
|
lmax, GFP_KERNEL);
|
|
}
|
|
copied = strncpy_from_user((void *)kenvp,
|
|
(void __user *)uenvp, len);
|
|
if (!copied)
|
|
continue;
|
|
else if (copied < 0) {
|
|
pr_alert("%s:%d: Cannot strncpy_from_user(len = %zd)\n",
|
|
__func__, __LINE__, len);
|
|
break;
|
|
}
|
|
if (!strncmp(env_var_name, (void *)kenvp, min(lenvar, len))) {
|
|
if (current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_DBG_MODE_DEBUG)
|
|
pr_info("ENVP: %s\n", (char *)kenvp);
|
|
if (*((char *)(kenvp + lenvar)) == '=')
|
|
return (char *)(kenvp + lenvar + 1);
|
|
if (current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_DBG_MODE_DEBUG)
|
|
pr_info("Env var \"%s\" is not \"%s\"\n",
|
|
(char *)kenvp, env_var_name);
|
|
}
|
|
}
|
|
kfree((void *)kenvp);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Setup PM error messaging language.
|
|
* 'max_len' - maximum expected env var length.
|
|
* Returns: -1 - if env.var. is not set;
|
|
* mask to apply to 'pm_sc_debug_mode' if env var is "set";
|
|
* 0 - otherwise.
|
|
*/
|
|
static
|
|
unsigned long check_PM_lang_setup(const char *env_var_name, const size_t max_len)
|
|
{
|
|
char *env_val;
|
|
|
|
env_val = pm_getenv(env_var_name, max_len);
|
|
if (!env_val)
|
|
return -1;
|
|
|
|
/* This is check for RUSSIAN language setup:
|
|
* ru_RU.KOI8-R, ru_RU.KOI8_R, ru_RU.KOI8R
|
|
* ru_RU.UTF-8, ru_RU.UTF_8, ru_RU.UTF8, ru_RU
|
|
*/
|
|
if (!strstr(env_val, "RU"))
|
|
return 0;
|
|
if (strstr(env_val, "ru_RU.KOI8"))
|
|
return PM_SC_ERR_MESSAGES_KOI8_R;
|
|
if (strstr(env_val, "ru_RU.UTF"))
|
|
return PM_SC_ERR_MESSAGES_RU_UTF;
|
|
if (strstr(env_val, "ru_RU"))
|
|
return PM_SC_ERR_MESSAGES_RU_UTF;
|
|
|
|
pr_err("Wrong value of the env var %s = %s\n", env_var_name, env_val);
|
|
pr_err("Legal values: ru_RU.UTF-8, ru_RU.UTF_8, ru_RU.UTF8, ru_RU,\n");
|
|
pr_err("\t\t ru_RU.KOI8-R, ru_RU.KOI8_R, ru_RU.KOI8R\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Checks for PM debug mode env var setup and outputs corresponding debug mask.
|
|
* 'max_len' - maximum expected env var length.
|
|
* Returns: mask to apply to 'pm_sc_debug_mode' if env var is "set";
|
|
* 0 - otherwise.
|
|
*/
|
|
static
|
|
unsigned int check_debug_value(const char *env_var_name, const size_t max_len)
|
|
{
|
|
char *env_val;
|
|
int value = 0, ret;
|
|
|
|
env_val = pm_getenv(env_var_name, max_len);
|
|
if (!env_val)
|
|
return 0;
|
|
|
|
ret = kstrtoint(env_val, 0, &value);
|
|
if (ret || value < 0)
|
|
pr_err("Wrong value of the env var %s = %s\n", env_var_name, env_val);
|
|
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* Checks for PM debug mode env var setup and outputs corresponding debug mask.
|
|
* 'max_len' - maximum expected env var length.
|
|
* Returns: mask to apply to 'pm_sc_debug_mode' if env var is "set";
|
|
* 0 - otherwise.
|
|
*/
|
|
static
|
|
unsigned long check_debug_mask(const char *env_var_name, const size_t max_len,
|
|
const unsigned long mask)
|
|
{
|
|
char *env_val;
|
|
|
|
env_val = pm_getenv(env_var_name, max_len);
|
|
if (!env_val)
|
|
return 0;
|
|
|
|
if (!*env_val || env_val[1]) /* single char expected as env var value */
|
|
goto wrong_val_out;
|
|
|
|
if ((*env_val == '1') || (*env_val == 'y') || (*env_val == 'Y'))
|
|
return mask;
|
|
if ((*env_val == '0') || (*env_val == 'n') || (*env_val == 'N'))
|
|
return ~mask;
|
|
|
|
wrong_val_out:
|
|
pr_alert("Wrong value of the env var %s = %s\n",
|
|
env_var_name, env_val);
|
|
pr_alert("Legal values: 0/1/y/n/Y/N\n");
|
|
return 0;
|
|
}
|
|
|
|
#define CHECK_DEBUG_MASK(mask_name) \
|
|
do { \
|
|
mask = check_debug_mask(#mask_name, 48, mask_name); \
|
|
if (mask) { \
|
|
if (mask & mask_name) /* positive mask */ \
|
|
context->pm_sc_debug_mode |= mask; \
|
|
else /* negative mask */ \
|
|
context->pm_sc_debug_mode &= mask; \
|
|
} \
|
|
} while (0)
|
|
|
|
static inline
|
|
void reset_PM_MM_default_setup(mm_context_t *context, int save_flag)
|
|
{
|
|
if (context->pm_sc_debug_mode & PM_MM_CHECK_4_DANGLING_POINTERS
|
|
&& save_flag != PM_MM_CHECK_4_DANGLING_POINTERS)
|
|
context->pm_sc_debug_mode &= ~PM_MM_CHECK_4_DANGLING_POINTERS;
|
|
if (context->pm_sc_debug_mode & PM_MM_ZEROING_FREED_POINTERS
|
|
&& save_flag != PM_MM_ZEROING_FREED_POINTERS)
|
|
context->pm_sc_debug_mode &= ~PM_MM_ZEROING_FREED_POINTERS;
|
|
if (context->pm_sc_debug_mode & PM_MM_EMPTYING_FREED_POINTERS
|
|
&& save_flag != PM_MM_EMPTYING_FREED_POINTERS)
|
|
context->pm_sc_debug_mode &= ~PM_MM_EMPTYING_FREED_POINTERS;
|
|
}
|
|
|
|
/* Checks if the given env var is defined in the environment.
|
|
* Returns: 1 - if "reset/disabled" env var found; 0 - otherwise.
|
|
*/
|
|
static inline
|
|
int pm_sc_debug_envp_check(mm_context_t *context)
|
|
{
|
|
unsigned long mask;
|
|
int reset_PM_MM_default; /* once env var encountered, we need to reset default setup */
|
|
|
|
/* Checking for env vars: */
|
|
mask = check_debug_mask("PM_SC_DBG_MODE_DISABLED", 48,
|
|
PM_SC_DBG_MODE_INIT);
|
|
if (mask & PM_SC_DBG_MODE_INIT) { /* positive mask */
|
|
context->pm_sc_debug_mode = PM_SC_DBG_MODE_INIT;
|
|
return 1;
|
|
}
|
|
|
|
mask = check_debug_mask("PM_SC_DBG_MODE_ALL", 48,
|
|
PM_SC_DBG_MODE_ALL);
|
|
if (mask & PM_SC_DBG_MODE_ALL) { /* positive mask */
|
|
context->pm_sc_debug_mode |= PM_SC_DBG_MODE_ALL;
|
|
pr_info("ENVP: PM_SC_DBG_MODE_ALL=1\n");
|
|
return 0;
|
|
}
|
|
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_MODE_DEBUG);
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_MODE_COMPLEX_WRAPPERS);
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_MODE_CHECK);
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_MODE_CONV_STRUCT);
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_MODE_SIGNALS);
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_MODE_NO_ERR_MESSAGES);
|
|
CHECK_DEBUG_MASK(PM_DIAG_MESSAGES_IN_JOURNAL);
|
|
CHECK_DEBUG_MASK(PM_DIAG_MESSAGES_IN_STDERR);
|
|
CHECK_DEBUG_MASK(PM_SC_CHECK4TAGS_IN_BUFF);
|
|
if (context->pm_sc_debug_mode & PM_SC_CHECK4TAGS_IN_BUFF) {
|
|
context->pm_sc_check4tags_max_size =
|
|
check_debug_value("PM_SC_CHECK4TAGS_MAX_SIZE", 48);
|
|
if (PM_SC_CHECK4TAGS_IN_BUFF && context->pm_sc_check4tags_max_size == 0)
|
|
context->pm_sc_check4tags_max_size = PM_SC_CHECK4TAGS_DEFAULT_MAX_SIZE;
|
|
}
|
|
/* Protected mode setup: */
|
|
CHECK_DEBUG_MASK(PROTECTED_MODE_SOFT);
|
|
if (!mask) {
|
|
/* Alias for backward compatibility: */
|
|
mask = check_debug_mask("PM_SC_DBG_MODE_WARN_ONLY",
|
|
48, PM_SC_DBG_MODE_WARN_ONLY);
|
|
if (mask) {
|
|
if (mask & PROTECTED_MODE_SOFT) /* positive mask */
|
|
context->pm_sc_debug_mode |= mask;
|
|
else /* negative mask */
|
|
context->pm_sc_debug_mode &= mask;
|
|
}
|
|
}
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_WARNINGS);
|
|
CHECK_DEBUG_MASK(PM_SC_DBG_WARNINGS_AS_ERRORS);
|
|
/* libc mmu control stuff: */
|
|
reset_PM_MM_default = 1;
|
|
CHECK_DEBUG_MASK(PM_MM_CHECK_4_DANGLING_POINTERS);
|
|
if (mask) {
|
|
reset_PM_MM_default_setup(context, PM_MM_CHECK_4_DANGLING_POINTERS);
|
|
reset_PM_MM_default = 0;
|
|
}
|
|
CHECK_DEBUG_MASK(PM_MM_ZEROING_FREED_POINTERS);
|
|
if (mask && reset_PM_MM_default) {
|
|
reset_PM_MM_default_setup(context, PM_MM_ZEROING_FREED_POINTERS);
|
|
reset_PM_MM_default = 0;
|
|
}
|
|
CHECK_DEBUG_MASK(PM_MM_EMPTYING_FREED_POINTERS);
|
|
if (mask && reset_PM_MM_default) {
|
|
reset_PM_MM_default_setup(context, PM_MM_EMPTYING_FREED_POINTERS);
|
|
reset_PM_MM_default = 0;
|
|
}
|
|
if (context->pm_sc_debug_mode & PM_MM_FREE_PTR_MODE_MASK == 0)
|
|
context->pm_sc_debug_mode |= PM_MM_DEFAULT_FREE_PTR_MODE; /* RM-18187 */
|
|
|
|
/* Language setup: */
|
|
mask = check_PM_lang_setup("LC_ALL", 48);
|
|
if ((long) mask >= 0) {
|
|
goto select_lang;
|
|
} else {
|
|
mask = check_PM_lang_setup("LC_MESSAGES", 48);
|
|
if (mask >= 0)
|
|
goto select_lang;
|
|
else
|
|
mask = check_PM_lang_setup("LANG", 48);
|
|
}
|
|
select_lang:
|
|
if ((long) mask > 0)
|
|
context->pm_sc_debug_mode |= mask;
|
|
|
|
if (context->pm_sc_debug_mode & PM_SC_ERR_MESSAGES_RU_UTF)
|
|
protected_error_list = &protected_error_list_RU[0];
|
|
else if (context->pm_sc_debug_mode & PM_SC_ERR_MESSAGES_KOI8_R)
|
|
protected_error_list = &protected_error_list_RU_KOI8[0];
|
|
else
|
|
protected_error_list = &protected_error_list_C[0];
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(protected_error_list_C) != PMSCERRMSG_NUMBER);
|
|
BUILD_BUG_ON(ARRAY_SIZE(protected_error_list_RU) != PMSCERRMSG_NUMBER);
|
|
BUILD_BUG_ON(ARRAY_SIZE(protected_error_list_RU_KOI8) != PMSCERRMSG_NUMBER);
|
|
|
|
context->pm_sc_debug_mode |= PM_SC_DBG_MODE_INIT;
|
|
|
|
if (IF_PM_DBG_MODE(PM_SC_DBG_MODE_DEBUG)) {
|
|
char *env_val;
|
|
|
|
env_val = pm_getenv("LC_ALL", 48 /*max_len*/);
|
|
if (env_val)
|
|
pr_info("LC_ALL: %s\n", env_val);
|
|
pr_info("\tpm_sc_debug_mode = 0x%lx\n",
|
|
context->pm_sc_debug_mode);
|
|
if (context->pm_sc_debug_mode & PM_SC_CHECK4TAGS_IN_BUFF
|
|
&& context->pm_sc_check4tags_max_size != 0)
|
|
pr_info("\tpm_sc_check4tags_max_size = %d\n",
|
|
context->pm_sc_check4tags_max_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int arch_init_pm_sc_debug_mode(const int debug_mask)
|
|
{
|
|
mm_context_t *context = ¤t->mm->context;
|
|
|
|
if (context->pm_sc_debug_mode & PM_SC_DBG_MODE_INIT)
|
|
return context->pm_sc_debug_mode & debug_mask;
|
|
|
|
/* Checking for env vars: */
|
|
if (pm_sc_debug_envp_check(context))
|
|
return 0;
|
|
|
|
return context->pm_sc_debug_mode & debug_mask;
|
|
}
|
|
|
|
static
|
|
int issue_prot_message_vl(const int msg_ID, const char *fmt, va_list argptr)
|
|
{
|
|
#define MSG_BUFF_SIZE 512
|
|
char message[MSG_BUFF_SIZE];
|
|
int ret = 0;
|
|
|
|
message[0] = '\0';
|
|
if (msg_ID >= 0 && msg_ID < PMSCERRMSG_FINAL) {
|
|
/* adding error ID to the message */
|
|
ret = snprintf(message, MSG_BUFF_SIZE, "[EPM#%d] ", msg_ID); /* offset */
|
|
if (ret > 0)
|
|
ret = vsnprintf(&message[ret], MSG_BUFF_SIZE - ret, fmt, argptr);
|
|
} else { /* no error ID required */
|
|
ret = vsnprintf(message, MSG_BUFF_SIZE, fmt, argptr);
|
|
}
|
|
if (ret <= 0) {
|
|
pr_err("%s:%d : %s//vsprintf() failed with error code (%d)\n",
|
|
__FILE__, __LINE__, __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (arch_init_pm_sc_debug_mode(PM_DIAG_MESSAGES_IN_JOURNAL))
|
|
pr_err("%s", message);
|
|
|
|
if (arch_init_pm_sc_debug_mode(PM_DIAG_MESSAGES_IN_STDERR)) {
|
|
struct file *fstderr = fget(2);
|
|
size_t msglen = strlen(message);
|
|
|
|
if (!fstderr) {
|
|
pr_err_ratelimited("%s:%d : kernel_write(fstderr, 0x%px, %zd) - could not write to stderr since it is not available\n",
|
|
__FILE__, __LINE__, &message, msglen);
|
|
return -EBADF;
|
|
}
|
|
|
|
ret = kernel_write(fstderr, message, msglen, NULL);
|
|
fput(fstderr);
|
|
|
|
if (ret == -ERESTARTSYS || ret == -ERESTARTNOINTR ||
|
|
ret == -ERESTARTNOHAND || ret == -ERESTART_RESTARTBLOCK) {
|
|
/* Write to tty will not go through anyway
|
|
* if signal_pending() so just return - we
|
|
* will get here again after syscall restart. */
|
|
} else if (ret <= 0) {
|
|
pr_err("%s:%d : kernel_write(2, 0x%px, %zd) failed with error code (%d)\n",
|
|
__FILE__, __LINE__, &message, msglen, ret);
|
|
if (arch_init_pm_sc_debug_mode(PM_DIAG_MESSAGES_IN_JOURNAL) == 0)
|
|
pr_err("%s", message);
|
|
} else if (ret < msglen) {
|
|
pr_err("%s:%d : kernel_write(2, 0x%px, %zd) failed; %d of %zd bytes written\n",
|
|
__FILE__, __LINE__, &message, msglen, ret, msglen);
|
|
if (arch_init_pm_sc_debug_mode(PM_DIAG_MESSAGES_IN_JOURNAL) == 0)
|
|
pr_err("%s", message);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline
|
|
int issue_prot_message(const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
int ret;
|
|
|
|
va_start(argptr, fmt);
|
|
ret = issue_prot_message_vl(-1, fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Delivering diagnostic messages that protected mode issues:
|
|
* header_type: 0 - no header; 1 - error header; 2 - warning header.
|
|
*/
|
|
void protected_mode_message(int header_type,
|
|
enum pm_syscall_err_msg_id MSG_ID, ...)
|
|
{
|
|
va_list argptr;
|
|
|
|
if ((arch_init_pm_sc_debug_mode(PM_SC_DBG_MODE_CHECK
|
|
| PM_DIAG_MESSAGES_IN_JOURNAL
|
|
| PM_DIAG_MESSAGES_IN_STDERR) == 0)
|
|
|| unlikely(current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_DBG_MODE_NO_ERR_MESSAGES))
|
|
return;
|
|
|
|
if (header_type) {
|
|
enum pm_syscall_err_msg_id header_id;
|
|
|
|
header_id = (header_type == 1) ? PMSCERRMSG_RUNTIME_ERROR
|
|
: PMSCERRMSG_RUNTIME_WARNING;
|
|
issue_prot_message("[PID#%d] %s: %s\n",
|
|
current->pid, current->comm,
|
|
protected_error_list[header_id]);
|
|
}
|
|
|
|
if (MSG_ID >= PMSCERRMSG_NUMBER) {
|
|
pr_err("%s:%d %s (%d)\n", __FILE__, __LINE__,
|
|
protected_error_list[PMSCERRMSG_ERR_ID], MSG_ID);
|
|
return;
|
|
}
|
|
|
|
va_start(argptr, MSG_ID);
|
|
issue_prot_message_vl(MSG_ID, protected_error_list[MSG_ID], argptr);
|
|
va_end(argptr);
|
|
}
|
|
|
|
|
|
/* Here we check that descriptor specified in argument #arg_num is read-able: */
|
|
static inline
|
|
int check_buffer_is_readable(const struct pt_regs *regs,
|
|
const int arg_num)
|
|
{
|
|
e2k_ptr_lo_t lo;
|
|
|
|
lo.word = (u64)regs->args[2 * arg_num - 1];
|
|
|
|
if (lo.r)
|
|
return 1;
|
|
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_DSCR_WITHOUT_READ_PERM,
|
|
sys_call_ID_to_name[regs->sys_num], arg_num);
|
|
return 0;
|
|
}
|
|
|
|
/* Here we check that descriptor specified in argument #arg_num is write-able: */
|
|
static inline
|
|
int check_buffer_is_writeable(const struct pt_regs *regs,
|
|
const int arg_num)
|
|
{
|
|
e2k_ptr_lo_t lo;
|
|
|
|
lo.word = (u64)regs->args[2 * arg_num - 1];
|
|
|
|
if (lo.w)
|
|
return 1;
|
|
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_DSCR_WITHOUT_WRITE_PERM,
|
|
sys_call_ID_to_name[regs->sys_num], arg_num);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Here we check for tagged words in the given buffer intended for write: */
|
|
static void check_buffer_for_tags(const void __user *buff,
|
|
const size_t count,
|
|
const struct pt_regs *regs)
|
|
{
|
|
void __user *ubuff = (void __user *)buff;
|
|
int offset, val_int, tag, i;
|
|
long val_long, val_hi;
|
|
long cnt;
|
|
|
|
if (!count)
|
|
return;
|
|
|
|
/* NB> We check word-aligned area within the buffer: */
|
|
offset = (unsigned long)buff & (sizeof(int) - 1);
|
|
ubuff += offset;
|
|
cnt = count - offset;
|
|
|
|
if (cnt <= 0)
|
|
return;
|
|
|
|
/* 1a) Check leading word if any: */
|
|
offset = (unsigned long)ubuff & (sizeof(unsigned long) - 1);
|
|
if (offset) {
|
|
if (get_user_tagged_4(val_int, tag, (int *)ubuff))
|
|
goto err_read;
|
|
if (tag)
|
|
goto err_out;
|
|
ubuff += sizeof(int);
|
|
cnt -= sizeof(int);
|
|
if (cnt <= 0)
|
|
return;
|
|
}
|
|
|
|
/* 1b) Check leading double-word if any: */
|
|
offset = (unsigned long)ubuff & (DESCRIPTOR_SIZE - 1);
|
|
if (cnt >= sizeof(unsigned long) && offset) {
|
|
if (get_user_tagged_8(val_long, tag, (unsigned long *)ubuff))
|
|
goto err_read;
|
|
if (tag)
|
|
goto err_out;
|
|
ubuff += sizeof(unsigned long);
|
|
cnt -= sizeof(unsigned long);
|
|
if (cnt <= 0)
|
|
return;
|
|
}
|
|
|
|
/* 2) Check main big buffer body: */
|
|
for (i = cnt / DESCRIPTOR_SIZE; i > 0; i--) {
|
|
if (get_user_tagged_16(val_long, val_hi, tag, (void *)ubuff))
|
|
goto err_read;
|
|
if (tag)
|
|
goto err_out;
|
|
ubuff += DESCRIPTOR_SIZE;
|
|
cnt -= DESCRIPTOR_SIZE;
|
|
}
|
|
|
|
if (cnt <= 0)
|
|
return;
|
|
|
|
/* 3a) Check trailing double-word if any: */
|
|
if (cnt >= sizeof(unsigned long)) {
|
|
if (get_user_tagged_8(val_long, tag, (unsigned long *)ubuff))
|
|
goto err_read;
|
|
if (tag)
|
|
goto err_out;
|
|
ubuff += sizeof(unsigned long);
|
|
cnt -= sizeof(unsigned long);
|
|
}
|
|
|
|
/* 3b) Check trailing word if any: */
|
|
if (cnt > 0) {
|
|
if (get_user_tagged_4(val_int, tag, (int *)ubuff))
|
|
goto err_read;
|
|
if (tag)
|
|
goto err_out;
|
|
}
|
|
|
|
return; /* no tag found */
|
|
|
|
err_read:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_ERR_FROM,
|
|
__func__, (unsigned long)ubuff);
|
|
return;
|
|
err_out:
|
|
if (tag) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_UNEXPECTED_TAG_IN_BUFF,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
tag, (unsigned long)buff, (int)count, (unsigned long)ubuff);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
/* Converts protected iov structure(s) to regular one(s):
|
|
* iov128 - protected array of iov structures;
|
|
* iov64 - regular array of iov structures;
|
|
* iov_len - # of iov elements in the input array.
|
|
*
|
|
* If 'regs' not NULL, do check for non-empty buffers in the iov structure.
|
|
* Result: error number or zero if converted OK.
|
|
*/
|
|
static int convert_iov(const void __user *iov128, const void __user *iov64,
|
|
const size_t iov_len,
|
|
const struct pt_regs *regs)
|
|
{
|
|
struct prot_iovec __user *iovec_p128 = (struct prot_iovec __user *)iov128;
|
|
struct iovec __user *iovec_p64 = (struct iovec __user *)iov64;
|
|
e2k_ptr_t buff;
|
|
__kernel_size_t buff_len;
|
|
void __user *ptr;
|
|
int tags, err = 0, i;
|
|
|
|
for (i = 0; i < iov_len; i++) {
|
|
err = get_user_tagged_16(buff.lo, buff.hi, tags, &iovec_p128->iov_base);
|
|
err = err ?: get_user(buff_len, &iovec_p128->iov_len);
|
|
if (err)
|
|
return err;
|
|
ptr = (void __user *)e2k_ptr_ptr(buff.lo, buff.hi, 0);
|
|
if (buff_len) {
|
|
if (unlikely((long)buff_len < 0)) {
|
|
return -EINVAL;
|
|
} else if (unlikely(tags != ETAGAPQ)) {
|
|
DbgSCP("bad iov_base 0x%llx:0x%llx tags 0x%x\n",
|
|
buff.lo, buff.hi, tags);
|
|
return -EFAULT;
|
|
} else if (unlikely(buff_len > e2k_ptr_size(buff.lo, buff.hi, 0))) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE, __func__,
|
|
"iov", buff_len, (size_t) e2k_ptr_size(buff.lo, buff.hi, 0));
|
|
return -EFAULT;
|
|
} else if (regs
|
|
&& unlikely(current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_CHECK4TAGS_IN_BUFF)
|
|
&& current->mm->context.pm_sc_check4tags_max_size >= buff_len) {
|
|
check_buffer_for_tags(ptr, buff_len, regs);
|
|
}
|
|
} else if (unlikely(tags && tags != ETAGAPQ && buff.lo)) {
|
|
DbgSCP("bad iov_base 0x%llx:0x%llx tags 0x%x or iov_len=%zd\n",
|
|
buff.lo, buff.hi, tags, buff_len);
|
|
return -EFAULT;
|
|
}
|
|
if (e2k_ptr_size(buff.lo, buff.hi, 0) < buff_len) {
|
|
DbgSCP("bad iov_base 0x%llx:0x%llx insufficient iov_len=%zd\n",
|
|
buff.lo, buff.hi, buff_len);
|
|
return -EFAULT;
|
|
}
|
|
err = put_user(ptr, &iovec_p64->iov_base);
|
|
err = err ?: put_user(buff_len, &iovec_p64->iov_len);
|
|
if (err)
|
|
return err;
|
|
iovec_p128 = (struct prot_iovec __user *)
|
|
((char __user *)iovec_p128 + sizeof(struct prot_iovec));
|
|
iovec_p64 = (struct iovec __user *)
|
|
((char __user *)iovec_p64 + sizeof(struct iovec));
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* # elements in msg_iov field of protected msghdr structure: */
|
|
static long get_prot_msghdr_iovlen(const void __user *prot_msghdr)
|
|
{
|
|
const struct protected_user_msghdr __user *umsghdr = prot_msghdr;
|
|
long iovlen;
|
|
int err;
|
|
|
|
if (unlikely(!umsghdr))
|
|
return 0L;
|
|
err = get_user(iovlen, &umsghdr->msg_iovlen);
|
|
if (unlikely(err)) {
|
|
return (long)err;
|
|
} else if (unlikely(iovlen <= 0)) { /* checking if 'iov' isn't empty */
|
|
e2k_ptr_t __user iov;
|
|
int tags;
|
|
|
|
err = get_user_tagged_16(iov.lo, iov.hi, tags, &umsghdr->msg_iov);
|
|
if (unlikely(err))
|
|
return (long)err;
|
|
if (unlikely(tags && (tags != ETAGAPQ)))
|
|
iovlen = -EINVAL;
|
|
else if (!tags)
|
|
iovlen = 0;
|
|
else
|
|
iovlen = -EMSGSIZE;
|
|
} else if (unlikely(iovlen > SOMAXCONN)) {
|
|
iovlen = -EMSGSIZE;
|
|
}
|
|
|
|
return iovlen;
|
|
}
|
|
|
|
static struct user_msghdr __user *convert_msghdr(
|
|
const void __user *prot_msghdr,
|
|
unsigned int size,
|
|
const char *syscall_name,
|
|
const char *arg_name,
|
|
void __user *user_buff,
|
|
const struct pt_regs *regs)
|
|
/* Converts user msghdr structure from protected to regular structure format.
|
|
* Outputs converted structure (allocated in user space if (user_buff == NULL)).
|
|
* 'prot_msghdr' - protected message header structure.
|
|
* 'size' - size of the input structure.
|
|
* 'user_buff' - buffer for converted structure in user space.
|
|
*/
|
|
{
|
|
long __user *args = (long __user *) user_buff;
|
|
struct protected_user_msghdr __user *msghdr_p128 =
|
|
(struct protected_user_msghdr __user *) prot_msghdr;
|
|
struct user_msghdr __user *msghdr_p64 = NULL;
|
|
struct prot_iovec __user *msg_iov;
|
|
e2k_ptr_t __user buff;
|
|
__kernel_size_t buff_len;
|
|
long iovlen;
|
|
int tags, err;
|
|
|
|
#define MASK_MSGHDR_TYPE 0x0773 /* type mask for struct msghdr */
|
|
#define MASK_MSGHDR_ALIGN 0x17ff /* alignment mask for msghdr structure */
|
|
#define MASK_MSGHDR_RW 0x2000 /* WRITE-only msg_flags field */
|
|
#define SIZE_MSGHDR sizeof(struct protected_user_msghdr)
|
|
|
|
if (!prot_msghdr)
|
|
return NULL;
|
|
if (size < SIZE_MSGHDR) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE, __func__,
|
|
"msghdr", size, SIZE_MSGHDR);
|
|
err = -EFAULT;
|
|
goto out_err;
|
|
}
|
|
|
|
/*
|
|
* Structures 'user_msghdr' and 'iovec' contain pointers inside;
|
|
* therefore they need to be converted to 64-bit mode
|
|
* and results to be saved in these structures afterwards.
|
|
*/
|
|
|
|
iovlen = get_prot_msghdr_iovlen(prot_msghdr);
|
|
if (unlikely(iovlen < 0)) {
|
|
DbgSCP("get_prot_msghdr_iovlen(0x%ld) returned %ld\n", (long)prot_msghdr, iovlen);
|
|
return ERR_PTR(iovlen);
|
|
}
|
|
|
|
/* Check for proper msg_control fields: */
|
|
err = get_user_tagged_16(buff.lo, buff.hi, tags, &msghdr_p128->msg_control);
|
|
err = err ?: get_user(buff_len, &msghdr_p128->msg_controllen);
|
|
if (err)
|
|
return ERR_PTR((long)err);
|
|
if (buff_len) {
|
|
if (unlikely(tags != ETAGAPQ)) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_NOT_DESCR_IN_STRUCT_FIELD,
|
|
syscall_name, tags, "user_msghdr",
|
|
"msg_control", buff.lo, buff.hi);
|
|
err = -EFAULT;
|
|
goto out_err;
|
|
} else if (buff_len > e2k_ptr_size(buff.lo, buff.hi, 0)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE, __func__,
|
|
"msg_control",
|
|
(size_t) e2k_ptr_size(buff.lo, buff.hi, 0), buff_len);
|
|
DbgSCP("bad msg_control 0x%lld:0x%lld buff_len %zd\n",
|
|
buff.lo, buff.hi, buff_len);
|
|
if (PM_SYSCALL_WARN_ONLY == 0) {
|
|
err = -EFAULT;
|
|
goto out_err;
|
|
}
|
|
buff_len = e2k_ptr_size(buff.lo, buff.hi, 0);
|
|
PROTECTED_MODE_MESSAGE(0, PMSCERRMSG_SC_ARG_COUNT_TRUNCATED, buff_len);
|
|
}
|
|
}
|
|
|
|
/* Allocating space on user stack for converted structures: */
|
|
if (!args)
|
|
args = get_user_space(sizeof(struct user_msghdr) +
|
|
iovlen * sizeof(struct iovec));
|
|
|
|
/* Convert struct msghdr: */
|
|
msghdr_p64 = (struct user_msghdr __user *) args;
|
|
err = convert_array_3(prot_msghdr, args, SIZE_MSGHDR, 7, 1, MASK_MSGHDR_TYPE,
|
|
MASK_MSGHDR_ALIGN, MASK_MSGHDR_RW, CONV_ARR_WRONG_DSCR_FLD);
|
|
if (err)
|
|
goto out_err;
|
|
|
|
err = get_user(msg_iov, &msghdr_p64->msg_iov);
|
|
if (err) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_ERR_FROM,
|
|
__func__, (long) &msghdr_p64->msg_iov);
|
|
return ERR_PTR((long)err);
|
|
}
|
|
if (msg_iov) {
|
|
struct iovec __user *iovec_p64;
|
|
|
|
/* Converting struct iovec from msghdr->msg_iov: */
|
|
iovec_p64 = (struct iovec __user *)
|
|
((char __user *) msghdr_p64 + sizeof(struct user_msghdr));
|
|
err = convert_iov(msg_iov, iovec_p64, iovlen, regs);
|
|
if (err) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_FIELD_STRUCT_IN_ARG_NAME,
|
|
syscall_name, "iovec", "user_msghdr", arg_name);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return ERR_PTR((long)err);
|
|
}
|
|
|
|
/* Assign converted iovec pointer to converted msghdr structure: */
|
|
err = put_user(iovec_p64, &msghdr_p64->msg_iov);
|
|
if (err) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_WRITE_AT,
|
|
__func__, (long) &msghdr_p64->msg_iov);
|
|
goto out_err;
|
|
}
|
|
} else {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_EMPTY_STRUCTURE_FIELD,
|
|
syscall_name, "msg_iov", "user_msghdr", "msghdr");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
|
|
return (struct user_msghdr __user *) args;
|
|
|
|
out_err:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_STRUCT_IN_ARG_NAME,
|
|
syscall_name, "protected_msghdr", "msghdr", arg_name);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return ERR_PTR((long)err);
|
|
}
|
|
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_clean_descriptors(void __user *addr,
|
|
unsigned long size,
|
|
const unsigned long flags,
|
|
const unsigned long unused_a4,
|
|
const unsigned long unused_a5,
|
|
const unsigned long unused_a6,
|
|
struct pt_regs *regs)
|
|
/* If (!flags) then 'addr' is a pointer to list of descriptors to clean. */
|
|
{
|
|
long rval = 0; /* syscall return value */
|
|
int descr_size;
|
|
unsigned long size_to_clean = size;
|
|
|
|
DbgSCP("addr=0x%lx, size=%ld, flags=0x%lx", (long)addr, size, flags);
|
|
|
|
if (unlikely(!addr || !size))
|
|
return 0L;
|
|
|
|
descr_size = e2k_ptr_size(regs->args[1], regs->args[2], 0);
|
|
if (!(flags & CLEAN_DESCRIPTORS_SINGLE))
|
|
size_to_clean *= sizeof(e2k_ptr_t);
|
|
if (descr_size < size_to_clean) {
|
|
PROTECTED_MODE_ALERT(PMCLNDSCRSMSG_WRONG_ARG_SIZE,
|
|
regs->args[1], regs->args[2], size, descr_size);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (flags & (CLEAN_DESCRIPTORS_SINGLE | CLEAN_DESCRIPTORS_NO_GARB_COLL) ==
|
|
(CLEAN_DESCRIPTORS_SINGLE | CLEAN_DESCRIPTORS_NO_GARB_COLL)) {
|
|
rval = mem_set_empty_tagged_dw(addr, size, 0x0baddead0baddead);
|
|
} else if (flags & CLEAN_DESCRIPTORS_SINGLE) {
|
|
e2k_ptr_t old_descriptor;
|
|
|
|
old_descriptor.lo = regs->args[1];
|
|
old_descriptor.hi = regs->args[2];
|
|
rval = clean_single_descriptor(old_descriptor);
|
|
} else if (!flags) {
|
|
rval = clean_descriptors(addr, size);
|
|
} else {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_WRONG_ARG_VALUE_LX,
|
|
"clean_descriptors", "flags", flags);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
if (rval == -EFAULT)
|
|
send_sig_info(SIGSEGV, SEND_SIG_PRIV, current);
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_clone(const unsigned long a1, /* flags */
|
|
const unsigned long a2, /* new_stackptr */
|
|
const unsigned long __user a3,/* parent_tidptr */
|
|
const unsigned long __user a4,/* child_tidptr */
|
|
const unsigned long __user a5,/* tls */
|
|
const unsigned long a6, /* unused */
|
|
struct pt_regs *regs)
|
|
{
|
|
int rval; /* syscall return value */
|
|
int size;
|
|
struct kernel_clone_args args = {};
|
|
|
|
DbgSCP("(fl=0x%lx, newsp=0x%lx, p/ch_tidptr=0x%lx/0x%lx, tls=0x%lx)\n",
|
|
a1, a2, a3, a4, a5);
|
|
/*
|
|
* User may choose to not pass additional arguments
|
|
* (tls, tid) at all for historical and compatibility
|
|
* reasons, so we do not fail if (a3), (a4), and (a5)
|
|
* pointers are bad.
|
|
*
|
|
* The fifth argument (tls) requires special handling:
|
|
*/
|
|
if (a1 & CLONE_SETTLS) {
|
|
int tls_size = 0;
|
|
|
|
if (a5 && !warn_if_not_descr(5, CHECK4DESCR_ERROR, regs))
|
|
tls_size = e2k_ptr_size(regs->args[9], regs->args[10], 0);
|
|
|
|
if (!tls_size) { /* bad pointer ? */
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_WRONG_ARG_VALUE_LX_TAG,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"tls", a5, PROT_SC_ARG_TAGS(5));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
size = e2k_ptr_curptr(regs->args[3], regs->args[4]);
|
|
|
|
args.flags = (a1 & ~CSIGNAL);
|
|
args.pidfd = (int __user *)a3;
|
|
args.child_tid = (int __user *)a4;
|
|
args.parent_tid = (int __user *)a3;
|
|
args.exit_signal = (a1 & CSIGNAL);
|
|
args.stack = a2 - size;
|
|
args.stack_size = size;
|
|
args.tls = a5;
|
|
|
|
/* passing size of array */
|
|
rval = kernel_clone(&args);
|
|
DbgSCP("rval = %d, sys_num = %d size=%d\n", rval, regs->sys_num, size);
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_execve(const char __user *filename,
|
|
unsigned long __user *u_argv,
|
|
unsigned long __user *u_envp,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
unsigned long __user *buf;
|
|
unsigned long __user *argv;
|
|
unsigned long __user *envp;
|
|
int size = 0, size2 = 0;
|
|
int argc = 0, envc = 0;
|
|
long rval; /* syscall return value */
|
|
|
|
/* Path to executable */
|
|
if (!filename)
|
|
return -EINVAL;
|
|
|
|
/* argv */
|
|
if (u_argv) {
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (!size)
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* envp */
|
|
if (u_envp) {
|
|
size2 = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (!size2)
|
|
return -EINVAL;
|
|
}
|
|
/*
|
|
* Note in the release 5.00 of the Linux man-pages:
|
|
* The use of a third argument to the main function
|
|
* is not specified in POSIX.1; according to POSIX.1,
|
|
* the environment should be accessed via the external
|
|
* variable environ(7).
|
|
*/
|
|
|
|
/* Count real number of entries in argv */
|
|
argc = count_descriptors(u_argv, size);
|
|
if (argc < 0)
|
|
return -EINVAL;
|
|
|
|
/* Count real number of entries in envc */
|
|
if (size2) {
|
|
envc = count_descriptors(u_envp, size2);
|
|
if (envc < 0)
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Allocate space on user stack for converting of
|
|
* descriptors in argv and envp to ints
|
|
*/
|
|
buf = get_user_space((argc + envc + 2) << 3);
|
|
argv = buf;
|
|
envp = &buf[argc + 1];
|
|
|
|
/*
|
|
* Convert descriptors in argv to ints.
|
|
* For statically-linked executables missing argv is allowed,
|
|
* therefore kernel doesn't return error in this case.
|
|
* For dynamically-linked executables missing argv is not
|
|
* allowed, because at least argv[0] is required by ldso for
|
|
* loading of executable. Protected ldso must check argv.
|
|
*/
|
|
if (argc) {
|
|
rval = convert_array(u_argv, argv, argc << 4, 1, argc, 0x3, 0x3);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"argv[]", 2);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
}
|
|
/* The array argv must be terminated by zero */
|
|
if (put_user(0, &argv[argc]))
|
|
return -EFAULT;
|
|
|
|
/*
|
|
* Convert descriptors in envp to ints
|
|
* envc can be zero without problems
|
|
*/
|
|
if (envc) {
|
|
rval = convert_array(u_envp, envp, envc << 4, 1, envc, 0x3, 0x3);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"envp[]", 3);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
}
|
|
/* The array envp must be terminated by zero */
|
|
if (put_user(0, &envp[envc]))
|
|
return -EFAULT;
|
|
|
|
rval = e2k_sys_execve(filename,
|
|
(const char __user *const __user *) argv,
|
|
(const char __user *const __user *) envp);
|
|
|
|
if (current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_DBG_MODE_COMPLEX_WRAPPERS) {
|
|
char *kfname = strcopy_from_user_prot_arg(filename, regs, 1);
|
|
|
|
if (kfname) {
|
|
DbgSCP(" rval = %ld filename=%s argv=%p envp=%p\n",
|
|
rval, kfname, argv, envp);
|
|
kfree(kfname);
|
|
}
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_execveat(const unsigned long dirfd, /*a1 */
|
|
const unsigned long __user pathname,/* a2 */
|
|
const unsigned long __user argv, /* a3 */
|
|
const unsigned long __user envp, /* a4 */
|
|
const unsigned long flags, /* a5 */
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
char __user *filename = (char __user *) pathname;
|
|
unsigned long __user *buf;
|
|
unsigned long __user *kargv;
|
|
unsigned long __user *kenvp;
|
|
unsigned long __user *u_argv = (unsigned long __user *) argv;
|
|
unsigned long __user *u_envp = (unsigned long __user *) envp;
|
|
int size = 0, size2 = 0;
|
|
int argc = 0, envc = 0;
|
|
long rval; /* syscall return value */
|
|
|
|
if (current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_DBG_MODE_COMPLEX_WRAPPERS) {
|
|
char *kfname = strcopy_from_user_prot_arg(filename, regs, 2);
|
|
|
|
if (kfname) {
|
|
DbgSCP(" dirfd=%ld path=%s argv=0x%lx envp=0x%lx flags=0x%lx\n",
|
|
dirfd, kfname, argv, envp, flags);
|
|
kfree(kfname);
|
|
}
|
|
}
|
|
|
|
/* Path to executable */
|
|
if (!filename)
|
|
return -EINVAL;
|
|
|
|
/* argv */
|
|
if (u_argv) {
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (!size)
|
|
return -EINVAL;
|
|
|
|
/* Count real number of entries in argv */
|
|
argc = count_descriptors(u_argv, size);
|
|
if (argc < 0)
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* envp */
|
|
if (u_envp) {
|
|
size2 = e2k_ptr_size(regs->args[7], regs->args[8], 0);
|
|
if (!size2)
|
|
return -EINVAL;
|
|
|
|
/* Count real number of entries in envc */
|
|
envc = count_descriptors(u_envp, size2);
|
|
if (envc < 0)
|
|
return -EINVAL;
|
|
}
|
|
|
|
DbgSCP(" argc=%d envc=%d\n", argc, envc);
|
|
|
|
/*
|
|
* Allocate space on user stack for converting of
|
|
* descriptors in argv and envp to ints
|
|
*/
|
|
buf = get_user_space((argc + envc + 2) << 3);
|
|
kargv = buf;
|
|
kenvp = &buf[argc + 1];
|
|
|
|
/*
|
|
* Convert descriptors in argv to ints.
|
|
* For statically-linked executables missing argv is allowed,
|
|
* therefore kernel doesn't return error in this case.
|
|
* For dynamically-linked executables missing argv is not
|
|
* allowed, because at least argv[0] is required by ldso for
|
|
* loading of executable. Protected ldso must check argv.
|
|
*/
|
|
if (argc) {
|
|
rval = convert_array(u_argv, kargv,
|
|
argc << 4, 1, argc, 0x3, 0x3);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"argv[]", 3);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
}
|
|
/* The array argv must be terminated by zero */
|
|
kargv[argc] = 0;
|
|
|
|
/*
|
|
* Convert descriptors in envp to ints
|
|
* envc can be zero without problems
|
|
*/
|
|
if (envc) {
|
|
rval = convert_array(u_envp, kenvp,
|
|
envc << 4, 1, envc, 0x3, 0x3);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"envp[]", 4);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
}
|
|
/* The array envp must be terminated by zero */
|
|
kenvp[envc] = 0;
|
|
|
|
rval = e2k_sys_execveat(dirfd, filename,
|
|
(char const __user *const __user *) kargv,
|
|
(char const __user *const __user *) kenvp, flags);
|
|
|
|
if (current->mm->context.pm_sc_debug_mode
|
|
& PM_SC_DBG_MODE_COMPLEX_WRAPPERS) {
|
|
char *kfname = strcopy_from_user_prot_arg(filename, regs, 2);
|
|
|
|
if (kfname) {
|
|
DbgSCP(" rval = %ld filename=%s argv=%p envp=%p\n",
|
|
rval, kfname, kargv, kenvp);
|
|
kfree(kfname);
|
|
}
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
|
|
static inline int check_prot_futex_arg_uninititialized(const long sys_num,
|
|
const long tags,
|
|
const int arg_num)
|
|
/* Returns 1 if arg is uninitialized; 0 otherwise */
|
|
{
|
|
u8 tag = (tags >> (arg_num * 8)) & 0xf;
|
|
|
|
if ((tag & 0x3) == ETAGDWS) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_UNEXP_ARG_TAG_ID,
|
|
sys_num, sys_call_ID_to_name[sys_num], (u8)tag, arg_num);
|
|
PROTECTED_MODE_MESSAGE(0, PMSCERRMSG_SC_ARG_MISSED_OR_UNINIT,
|
|
arg_num);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_futex(const unsigned long __user a1, /* uaddr */
|
|
const unsigned long a2, /* futex_op */
|
|
const unsigned long a3, /* val */
|
|
const unsigned long __user la4, /* timeout/val2 */
|
|
const unsigned long __user la5, /* uaddr2 */
|
|
const unsigned long a6, /* val3 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
int cmd;
|
|
unsigned long __user a4 = la4;
|
|
unsigned long __user a5 = la5;
|
|
long sys_num = regs->sys_num;
|
|
long rval = 0;
|
|
long tags = regs->tags;
|
|
|
|
cmd = a2 & FUTEX_CMD_MASK;
|
|
|
|
/* Check for optional args must be initialized: */
|
|
|
|
switch (cmd) {
|
|
case FUTEX_FD:
|
|
case FUTEX_TRYLOCK_PI:
|
|
case FUTEX_UNLOCK_PI:
|
|
case FUTEX_WAKE:
|
|
break; /* The arguments timeout, uaddr2, and val3 are ignored. */
|
|
|
|
case FUTEX_WAIT:
|
|
rval = check_prot_futex_arg_uninititialized(sys_num, tags, 4 /*timeout*/);
|
|
/* The arguments uaddr2, and val3 are ignored. */
|
|
break;
|
|
|
|
case FUTEX_WAIT_REQUEUE_PI:
|
|
case FUTEX_REQUEUE:
|
|
rval = check_prot_futex_arg_uninititialized(sys_num, tags, 4 /*timeout*/);
|
|
rval |= check_prot_futex_arg_uninititialized(sys_num, tags, 5 /*uaddr2*/);
|
|
/* The argument val3 is ignored. */
|
|
break;
|
|
|
|
case FUTEX_CMP_REQUEUE:
|
|
case FUTEX_CMP_REQUEUE_PI:
|
|
case FUTEX_WAKE_OP:
|
|
/* ALL ARGUMENYS ARE USED */
|
|
rval = check_prot_futex_arg_uninititialized(sys_num, tags, 4 /*timeout*/);
|
|
rval |= check_prot_futex_arg_uninititialized(sys_num, tags, 5 /*uaddr2*/);
|
|
rval |= check_prot_futex_arg_uninititialized(sys_num, tags, 6 /*val3*/);
|
|
break;
|
|
|
|
case FUTEX_WAIT_BITSET:
|
|
rval = check_prot_futex_arg_uninititialized(sys_num, tags, 4 /*timeout*/);
|
|
/* The argument uaddr2 is ignored. */
|
|
rval |= check_prot_futex_arg_uninititialized(sys_num, tags, 6 /*val3*/);
|
|
break;
|
|
|
|
case FUTEX_WAKE_BITSET:
|
|
/* The argument timeout is ignored. */
|
|
/* The argument uaddr2 is ignored. */
|
|
rval |= check_prot_futex_arg_uninititialized(sys_num, tags, 6 /*val3*/);
|
|
break;
|
|
|
|
case FUTEX_LOCK_PI:
|
|
/* case FUTEX_LOCK_PI2: */
|
|
rval = check_prot_futex_arg_uninititialized(sys_num, tags, 4 /*timeout*/);
|
|
/* The arguments val, uaddr2, and val3 are ignored. */
|
|
break;
|
|
}
|
|
if (rval)
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, 0, EFAULT);
|
|
|
|
if (la4 && (cmd == FUTEX_WAIT ||
|
|
cmd == FUTEX_WAIT_BITSET ||
|
|
cmd == FUTEX_LOCK_PI ||
|
|
cmd == FUTEX_WAIT_REQUEUE_PI)) {
|
|
/*
|
|
* These commands assume la4 must be a pointer. Let's check it:
|
|
*/
|
|
unsigned long arg7 = regs->args[7];
|
|
|
|
if (NOT_PTR(4) && !NULL_PTR(4, 7)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NOT_DESCR_IN_SC_ARG_NAME_TAG,
|
|
sys_call_ID_to_name[sys_num],
|
|
"timeout", PROT_SC_ARG_TAGS(4));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = -EINVAL;
|
|
}
|
|
}
|
|
if (la5 && (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
|
|
cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP ||
|
|
cmd == FUTEX_WAIT_REQUEUE_PI)) {
|
|
/*
|
|
* These commands assume la5 must be a pointer. Let's check it:
|
|
*/
|
|
unsigned long arg9 = regs->args[9];
|
|
|
|
if (NOT_PTR(5) && !NULL_PTR(5, 9)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NOT_DESCR_IN_SC_ARG_NAME_TAG,
|
|
sys_call_ID_to_name[sys_num],
|
|
"uaddr2", PROT_SC_ARG_TAGS(5));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = -EINVAL;
|
|
}
|
|
}
|
|
rval = sys_futex((u32 __user *) a1, a2, a3,
|
|
(struct __kernel_timespec __user *) a4,
|
|
(u32 __user *) a5, a6);
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_getgroups(const long a1, /* size */
|
|
const unsigned long __user a2, /* list[] */
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval; /* syscall return value */
|
|
int bufsize;
|
|
|
|
DbgSCP(" (size=%ld, list[]=0x%lx) ", a1, a2);
|
|
|
|
if (a1 < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_WRONG_ARG_VALUE_LX,
|
|
sys_call_ID_to_name[regs->sys_num], "size", a1);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
if (a2 && (PROT_SC_ARG_TAGS(2) != ETAGAPQ)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NOT_DESCR_IN_SC_ARG_NAME_TAG,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"list[]", PROT_SC_ARG_TAGS(2));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EFAULT;
|
|
}
|
|
/*
|
|
* Here we check that list size is enough to receive 'size' gid's:
|
|
*/
|
|
bufsize = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if ((a1 > 0) && (bufsize < (a1 * sizeof(gid_t)))) {
|
|
if (!size_exceeds_descr_max_capacity((a1 * sizeof(gid_t)), "size", a1, regs))
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"list[]", bufsize, (size_t)(a1 * sizeof(gid_t)));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_getgroups(a1, (gid_t __user *) a2);
|
|
DbgSCP("rval = %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_setgroups(const long a1, /* size */
|
|
const unsigned long __user a2, /* list[] */
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval; /* syscall return value */
|
|
int bufsize;
|
|
|
|
DbgSCP(" (size=%ld, list[]=0x%lx) ", a1, a2);
|
|
|
|
if (a1 < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_WRONG_ARG_VALUE_LX,
|
|
sys_call_ID_to_name[regs->sys_num], "size", a1);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
if (a2 && (PROT_SC_ARG_TAGS(2) != ETAGAPQ)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NOT_DESCR_IN_SC_ARG_NAME_TAG,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"list[]", PROT_SC_ARG_TAGS(2));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EFAULT;
|
|
}
|
|
/*
|
|
* Here we check that list size is enough to receive 'size' gid's:
|
|
*/
|
|
bufsize = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if ((a1 > 0) && (bufsize < (a1 * sizeof(gid_t)))) {
|
|
if (!size_exceeds_descr_max_capacity((a1 * sizeof(gid_t)), "size", a1, regs))
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"list[]", bufsize, (size_t)(a1 * sizeof(gid_t)));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_setgroups(a1, (gid_t __user *) a2);
|
|
DbgSCP("rval = %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
|
|
/*
|
|
* This function converts protected structure 'iov' into regular one
|
|
* alone with the check for validity of iov/iovcnt arguments to system calls,
|
|
* taking iovec structure at input (like readv/writev and so).
|
|
* iovcnt - number of buffers from the file assiciated to read/write.
|
|
* It returns converted structure pointer if OK; NULL pointer otherwise.
|
|
*/
|
|
static inline
|
|
long __user *convert_prot_iovec_struct(const unsigned long __user iov,
|
|
const unsigned long iovcnt,
|
|
const unsigned int arg_num,
|
|
const unsigned char *arg_name,
|
|
const struct pt_regs *regs,
|
|
const int check_buff)
|
|
{
|
|
unsigned long descr_lo;
|
|
unsigned long descr_hi;
|
|
const int nr_segs = iovcnt;
|
|
long __user *new_arg = NULL;
|
|
int size;
|
|
long rval; /* syscall return value */
|
|
|
|
if (unlikely(nr_segs > UIO_MAXIOV)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_COUNT_EXCEEDS_LIMIT,
|
|
sys_call_ID_to_name[regs->sys_num], nr_segs, UIO_MAXIOV);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return ERR_PTR(-EINVAL);
|
|
} else if (unlikely(nr_segs < 0)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NEGATIVE_SIZE_VALUE, regs->sys_num,
|
|
sys_call_ID_to_name[regs->sys_num], iovcnt, 2/*arg#*/);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
descr_lo = regs->args[2 * arg_num - 1];
|
|
descr_hi = regs->args[2 * arg_num];
|
|
|
|
size = e2k_ptr_size(descr_lo, descr_hi, 0);
|
|
if (size < (sizeof(struct prot_iovec) * nr_segs)) {
|
|
if (!size_exceeds_descr_max_capacity((sizeof(struct prot_iovec) * iovcnt),
|
|
"iovcnt", iovcnt, regs))
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_INSUFFICIENT_STRUCT_SIZE,
|
|
sys_call_ID_to_name[regs->sys_num], "iov",
|
|
size, (sizeof(struct prot_iovec) * nr_segs));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(arg_num, regs);
|
|
return NULL;
|
|
}
|
|
|
|
new_arg = get_user_space(nr_segs * sizeof(struct iovec));
|
|
rval = convert_iov((void __user *)iov, new_arg, nr_segs,
|
|
check_buff ? regs : NULL);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_STRUCT_IN_ARG_NAME,
|
|
sys_call_ID_to_name[regs->sys_num], "iov", arg_name);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return ERR_PTR(rval);
|
|
}
|
|
return new_arg;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_readv(const unsigned long a1, /* fd */
|
|
const unsigned long __user a2, /* iov */
|
|
const unsigned long a3, /* iovcnt */
|
|
const unsigned long a4, /* unused */
|
|
const unsigned long a5, /* unused */
|
|
const unsigned long a6, /* unused */
|
|
const struct pt_regs *regs)
|
|
{
|
|
/*
|
|
* sys_readv(unsigned long fd, const struct iovec __user *vec,
|
|
* unsigned long nr_segs)
|
|
* struct iovec {
|
|
* void __user *iov_base;
|
|
* __kernel_size_t iov_len;
|
|
* };
|
|
*/
|
|
const int nr_segs = (int) a3;
|
|
long __user *new_arg;
|
|
long rval; /* syscall return value */
|
|
|
|
if (!nr_segs)
|
|
return 0;
|
|
|
|
if ((int) a1 < 0)
|
|
return -EBADF;
|
|
if (nr_segs < 0)
|
|
return -EINVAL;
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs)) {
|
|
new_arg = (long __user *)a2;
|
|
} else {
|
|
new_arg = convert_prot_iovec_struct(a2, a3, 2, "iov", regs, 0);
|
|
if (unlikely(IS_ERR(new_arg)))
|
|
return PTR_ERR(new_arg);
|
|
else if (!new_arg)
|
|
return -EFAULT;
|
|
}
|
|
|
|
rval = sys_readv(a1, (const struct iovec __user *) new_arg, nr_segs);
|
|
|
|
DbgSCP(" rval = %ld new_arg=%px\n", rval, new_arg);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_preadv(const unsigned long a1, /* fd */
|
|
const unsigned long __user a2, /* iov */
|
|
const unsigned long a3, /* iovcnt */
|
|
const unsigned long a4, /* offset_l */
|
|
const unsigned long a5, /* offset_h */
|
|
const unsigned long a6, /* unused */
|
|
const struct pt_regs *regs)
|
|
{
|
|
const int nr_segs = (int) a3;
|
|
long __user *new_arg;
|
|
long rval; /* syscall return value */
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs)) {
|
|
new_arg = (long __user *)a2;
|
|
} else {
|
|
new_arg = convert_prot_iovec_struct(a2, a3, 2, "iov", regs, 0);
|
|
if (unlikely(IS_ERR(new_arg)))
|
|
return PTR_ERR(new_arg);
|
|
else if (!new_arg)
|
|
return -EFAULT;
|
|
}
|
|
|
|
rval = sys_preadv(a1, (const struct iovec __user *) new_arg, nr_segs, a4, a5);
|
|
|
|
DbgSCP(" rval = %ld new_arg=%px\n", rval, new_arg);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_write(const unsigned int fd,
|
|
const void __user *buff,
|
|
const size_t count,
|
|
const unsigned long a4, /* unused */
|
|
const unsigned long a5, /* unused */
|
|
const unsigned long a6, /* unused */
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval; /* syscall return value */
|
|
|
|
/* NB> Argument correctness has been checked by generic checks in ttable_entry8_C() */
|
|
|
|
if (count && !check_buffer_is_readable(regs, 2))
|
|
return -EFAULT;
|
|
|
|
if (unlikely(current->mm->context.pm_sc_debug_mode & PM_SC_CHECK4TAGS_IN_BUFF)
|
|
&& count != 0
|
|
&& current->mm->context.pm_sc_check4tags_max_size >= count)
|
|
check_buffer_for_tags(buff, count, regs);
|
|
|
|
rval = sys_write(fd, buff, count);
|
|
|
|
DbgSCP(" rval = %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_writev(const unsigned long a1, /* fd */
|
|
const unsigned long __user a2, /* iov */
|
|
const unsigned long a3, /* iovcnt */
|
|
const unsigned long a4, /* unused */
|
|
const unsigned long a5, /* unused */
|
|
const unsigned long a6, /* unused */
|
|
const struct pt_regs *regs)
|
|
{
|
|
const int nr_segs = (int) a3;
|
|
long __user *new_arg;
|
|
long rval; /* syscall return value */
|
|
|
|
if (!nr_segs)
|
|
return 0;
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs)) {
|
|
new_arg = (long __user *)a2;
|
|
} else {
|
|
new_arg = convert_prot_iovec_struct(a2, a3, 2, "iov", regs, 1);
|
|
if (unlikely(IS_ERR(new_arg)))
|
|
return PTR_ERR(new_arg);
|
|
else if (!new_arg)
|
|
return -EFAULT;
|
|
}
|
|
|
|
rval = sys_writev(a1, (const struct iovec __user *) new_arg, nr_segs);
|
|
|
|
DbgSCP(" rval = %ld new_arg=%px\n", rval, new_arg);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_pwritev(const unsigned long a1, /* fd */
|
|
const unsigned long __user a2, /* iov */
|
|
const unsigned long a3, /* iovcnt */
|
|
const unsigned long a4, /* offset_l */
|
|
const unsigned long a5, /* offset_h */
|
|
const unsigned long a6, /* unused */
|
|
const struct pt_regs *regs)
|
|
{
|
|
const int nr_segs = (int) a3;
|
|
long __user *new_arg;
|
|
long rval; /* syscall return value */
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs)) {
|
|
new_arg = (long __user *)a2;
|
|
} else {
|
|
new_arg = convert_prot_iovec_struct(a2, a3, 2, "iov", regs, 1);
|
|
if (unlikely(IS_ERR(new_arg)))
|
|
return PTR_ERR(new_arg);
|
|
else if (!new_arg)
|
|
return -EFAULT;
|
|
}
|
|
|
|
rval = sys_pwritev(a1, (const struct iovec __user *) new_arg, nr_segs, a4, a5);
|
|
|
|
DbgSCP(" rval = %ld new_arg=%px\n", rval, new_arg);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_preadv2(const unsigned long fd,
|
|
const unsigned long __user iov,
|
|
const unsigned long iovcnt,
|
|
const unsigned long offset_l,
|
|
const unsigned long offset_h,
|
|
const unsigned long flags,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long __user *new_arg;
|
|
long rval; /* syscall return value */
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs)) {
|
|
new_arg = (long __user *)iov;
|
|
} else {
|
|
new_arg = convert_prot_iovec_struct(iov, iovcnt, 2, "iov", regs, 0);
|
|
if (unlikely(IS_ERR(new_arg)))
|
|
return PTR_ERR(new_arg);
|
|
else if (!new_arg)
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_preadv2(fd, (const struct iovec __user *) new_arg, iovcnt,
|
|
offset_l, offset_h, (rwf_t) flags);
|
|
|
|
DbgSCP(" rval = %ld new_arg=%px\n", rval, new_arg);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_pwritev2(const unsigned long fd,
|
|
const unsigned long __user iov,
|
|
const unsigned long iovcnt,
|
|
const unsigned long offset_l,
|
|
const unsigned long offset_h,
|
|
const unsigned long flags,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long __user *new_arg;
|
|
long rval; /* syscall return value */
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs)) {
|
|
new_arg = (long __user *)iov;
|
|
} else {
|
|
new_arg = convert_prot_iovec_struct(iov, iovcnt, 2, "iov", regs, 1);
|
|
if (unlikely(IS_ERR(new_arg)))
|
|
return PTR_ERR(new_arg);
|
|
else if (!new_arg)
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_pwritev2(fd, (const struct iovec __user *) new_arg, iovcnt,
|
|
offset_l, offset_h, (rwf_t) flags);
|
|
|
|
DbgSCP(" rval = %ld new_arg=%px\n", rval, new_arg);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_uselib(const char __user *library,
|
|
const unsigned long __user a2, /* umdd */
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
umdd_t __user *umdd = (umdd_t __user *) a2;
|
|
kmdd_t kmdd;
|
|
int rval; /* syscall return value */
|
|
|
|
if (!library || !a2 || !e2k_ptr_str(regs->args[1], regs->args[2]))
|
|
return -EINVAL;
|
|
|
|
if (current->thread.flags & E2K_FLAG_3P_ELF32)
|
|
rval = sys_load_cu_elf32_3P(library, &kmdd);
|
|
else
|
|
rval = sys_load_cu_elf64_3P(library, &kmdd);
|
|
|
|
if (rval) {
|
|
DbgSCP("could not load library err #%d\n", rval);
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_FAILED_TO_LOAD_LIBRARY,
|
|
sys_call_ID_to_name[regs->sys_num]);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
BUG_ON(kmdd.cui == 0);
|
|
|
|
rval = PUT_USER_AP(&umdd->mdd_got, kmdd.got_addr, kmdd.got_len, 0, RW_ENABLE);
|
|
|
|
if (kmdd.init_got_point) {
|
|
rval = rval ?: PUT_USER_PL(&umdd->mdd_init_got,
|
|
kmdd.init_got_point,
|
|
kmdd.cui);
|
|
} else {
|
|
rval = rval ?: put_user(0L, &AW(umdd->mdd_init_got.lo));
|
|
rval = rval ?: put_user(0L, &AW(umdd->mdd_init_got.hi));
|
|
}
|
|
if (rval) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_FATAL_WRITE_AT,
|
|
sys_call_ID_to_name[regs->sys_num], umdd);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EFAULT);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
long protected_sys_mremap(const unsigned long __user old_address,
|
|
const unsigned long old_size,
|
|
const unsigned long new_size,
|
|
const unsigned long flags,
|
|
const unsigned long __user new_address,
|
|
const unsigned long unused6,
|
|
struct pt_regs *regs)
|
|
{
|
|
long rval = -EINVAL;
|
|
int ptr_size;
|
|
e2k_addr_t base;
|
|
|
|
if (old_address & ~PAGE_MASK)
|
|
goto nr_mremap_err;
|
|
|
|
ptr_size = e2k_ptr_size(regs->args[1], regs->args[2], 0);
|
|
|
|
DbgSCP("old_address=0x%lx old_size=0x%lx new_size=0x%lx flags=0x%lx new_address=0x%lx\n",
|
|
old_address, old_size, new_size, flags, new_address);
|
|
|
|
if (old_size && ptr_size < old_size) {
|
|
/* Reject, if user tries to remap more than allocated. */
|
|
PROTECTED_MODE_WARNING(PMMMAPMSG_CANT_REMAP_OVER_ALLOCATED,
|
|
sys_call_ID_to_name[regs->sys_num], old_size, ptr_size);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
rval = -EFAULT;
|
|
goto nr_mremap_err;
|
|
}
|
|
if (e2k_ptr_itag(regs->args[1]) != AP_ITAG) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_DESCR_IN_STACK,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
old_address, "old_address");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
goto nr_mremap_err;
|
|
}
|
|
base = sys_mremap(old_address, old_size, new_size, flags, new_address);
|
|
if (base & ~PAGE_MASK) { /* this is error code */
|
|
rval = base;
|
|
goto nr_mremap_err;
|
|
} else {
|
|
regs->rval1 = make_ap_lo(base, new_size, 0,
|
|
e2k_ptr_rw(regs->args[1]));
|
|
regs->rval2 = make_ap_hi(base, new_size, 0,
|
|
e2k_ptr_rw(regs->args[1]));
|
|
regs->rv1_tag = E2K_AP_LO_ETAG;
|
|
regs->rv2_tag = E2K_AP_HI_ETAG;
|
|
regs->return_desk = 1;
|
|
rval = 0;
|
|
}
|
|
if (old_address != base
|
|
&& arch_init_pm_sc_debug_mode(PM_MM_CHECK_4_DANGLING_POINTERS)) {
|
|
e2k_ptr_t old_descriptor;
|
|
|
|
old_descriptor.lo = regs->args[1];
|
|
old_descriptor.hi = regs->args[2];
|
|
rval = clean_single_descriptor(old_descriptor);
|
|
if (rval) {
|
|
PROTECTED_MODE_WARNING(PMSCWARN_PROC_RETURNED_ERROR,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"clean_single_descriptor()", rval);
|
|
PROTECTED_MODE_MESSAGE(1, PMCLNDSCRSMSG_EXITED_WITH_ERR,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
old_address, old_size, rval);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = 0;
|
|
}
|
|
}
|
|
|
|
DbgSCP("rval = %ld regs->rval = 0x%lx : 0x%lx\n",
|
|
rval, regs->rval1, regs->rval2);
|
|
return rval;
|
|
|
|
nr_mremap_err:
|
|
regs->rval1 = rval;
|
|
regs->rval2 = 0;
|
|
regs->rv1_tag = E2K_NUMERIC_ETAG;
|
|
regs->rv2_tag = E2K_NUMERIC_ETAG;
|
|
regs->return_desk = 1;
|
|
DbgSCP("rval = %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
/*
|
|
* The structure of the second argument to socket call depends on
|
|
* the socket call number.
|
|
* This function calculates mask/align type arguments to process
|
|
* the structure by 'convert+array'.
|
|
*/
|
|
notrace __section(".entry.text")
|
|
static void get_socketcall_mask(long call, long *mask_type, long *mask_align,
|
|
int *fields)
|
|
{
|
|
switch (call) {
|
|
/* Following calls don't require args conversion:
|
|
* case SYS_SOCKET:
|
|
* err = sys_socket(a[0], a[1], a[2]);
|
|
* case SYS_LISTEN:
|
|
* err = sys_listen(a[0], a[1]);
|
|
* case SYS_SHUTDOWN:
|
|
* err = sys_shutdown(a[0], a[1]);
|
|
* break;
|
|
*/
|
|
case SYS_BIND:
|
|
/* err = sys_bind(a[0], */
|
|
/* (struct sockaddr __user *) a[1], a[2]); */
|
|
case SYS_CONNECT:
|
|
/* err = sys_connect(a[0], */
|
|
/* (struct sockaddr __user *) a[1], a[2]); */
|
|
*mask_type = 0x1d;
|
|
*mask_align = 0x1f;
|
|
*fields = 3;
|
|
break;
|
|
case SYS_ACCEPT:
|
|
/* err = sys_accept(a[0], */
|
|
/* (struct sockaddr __user *) a[1], */
|
|
/* (int __user*) a[2]); */
|
|
case SYS_GETSOCKNAME:
|
|
/* err = sys_getsockname(a[0], */
|
|
/* (struct sockaddr __user*) a[1], */
|
|
/* (int __user *) a[2]); */
|
|
case SYS_GETPEERNAME:
|
|
/* err = sys_getpeername(a[0], */
|
|
/* (struct sockaddr __user *) a[1], */
|
|
/* (int __user *)a[2]); */
|
|
*mask_type = 0x3d;
|
|
*mask_align = 0x3f;
|
|
*fields = 3;
|
|
break;
|
|
case SYS_ACCEPT4:
|
|
*mask_type = 0x7d;
|
|
*mask_align = 0xff;
|
|
*fields = 4;
|
|
/* err = sys_accept4(a[0], */
|
|
/* (struct sockaddr __user *) a[1], */
|
|
/* (int __user*) a[2] */
|
|
/* (int) a[3]); */
|
|
break;
|
|
case SYS_SOCKETPAIR:
|
|
*mask_type = 0xd5;
|
|
*mask_align = 0xf5;
|
|
*fields = 4;
|
|
/*err = sys_socketpair(a[0], a[1], a[2], */
|
|
/* (int __user *)a[3]); */
|
|
break;
|
|
case SYS_SEND:
|
|
*mask_type = 0x5d;
|
|
*mask_align = 0x5f;
|
|
*fields = 4;
|
|
/* err = sys_send(a[0], (void __user *) a[1], a[2], */
|
|
/* a[3]); */
|
|
break;
|
|
case SYS_SENDTO:
|
|
*mask_type = 0x75d;
|
|
*mask_align = 0x7df;
|
|
*fields = 6;
|
|
/* err = sys_sendto(a[0], (void __user *) a[1], a[2], */
|
|
/* a[3], (struct sockaddr __user *) a[4], a[5]); */
|
|
break;
|
|
case SYS_RECV:
|
|
*mask_type = 0x5d;
|
|
*mask_align = 0x5f;
|
|
*fields = 4;
|
|
/* err = sys_recv(a[0], (void __user *) a[1], */
|
|
/* a[2], a[3]); */
|
|
break;
|
|
case SYS_RECVFROM:
|
|
*mask_type = 0xf5d;
|
|
*mask_align = 0xfdf;
|
|
*fields = 6;
|
|
/* err = sys_recvfrom(a[0], (void __user *) a[1], a[2], */
|
|
/* a[3], (struct sockaddr __user *) a[4], */
|
|
/* (int __user *) a[5]); */
|
|
break;
|
|
case SYS_SETSOCKOPT:
|
|
*mask_type = 0x1d5;
|
|
*mask_align = 0x1f5;
|
|
*fields = 5;
|
|
/* err = sys_setsockopt(a[0], a[1], a[2], */
|
|
/* (char __user *)a[3], a[4]); */
|
|
break;
|
|
case SYS_GETSOCKOPT:
|
|
*mask_type = 0x3d5;
|
|
*mask_align = 0x3f5;
|
|
*fields = 5;
|
|
/* err = sys_getsockopt(a[0], a[1], a[2], */
|
|
/* (char __user *) a[3], (int __user *)a[4]); */
|
|
break;
|
|
case SYS_SENDMSG:
|
|
/* err = sys_sendmsg(a[0], */
|
|
/* (struct msghdr __user *) a[1], a[2]); */
|
|
case SYS_RECVMSG:
|
|
/* err = sys_recvmsg(a[0], */
|
|
/* (struct msghdr __user *) a[1], a[2]); */
|
|
*mask_type = 0x1d;
|
|
*mask_align = 0x1f;
|
|
*fields = 3;
|
|
break;
|
|
default:
|
|
DbgSCP("Empty masks used for socketcall #%ld\n", call);
|
|
*mask_type = 0x0;
|
|
*mask_align = 0x0;
|
|
*fields = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_socketcall(const unsigned long a1, /* call */
|
|
const unsigned long __user *a2, /* args */
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
unsigned long __user *args;
|
|
int size;
|
|
long mask_type, mask_align;
|
|
int fields;
|
|
long rval; /* syscall return value */
|
|
struct protected_user_msghdr __user *prot_msghdr;
|
|
struct user_msghdr __user *converted_msghdr;
|
|
|
|
get_socketcall_mask(a1, &mask_type, &mask_align, &fields);
|
|
|
|
if (!a2) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_CMD_WRONG_ARG_VALUE_LX,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"call", (int) a1, "args", a2);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EFAULT;
|
|
}
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 1 /*min_size*/);
|
|
/* NB> `convert_array' below will check if size is large
|
|
* enough for this request.
|
|
*
|
|
* Need an additional conversions of arguments
|
|
* for syscalls recvmsg/sendmsg
|
|
*/
|
|
if ((a1 == SYS_SENDMSG) || (a1 == SYS_RECVMSG)) {
|
|
e2k_ptr_t descr; /* protected_user_msghdr in the arg array */
|
|
void __user *ptr;
|
|
long iov_len;
|
|
int tags;
|
|
|
|
rval = get_user_tagged_16(descr.lo, descr.hi, tags, &a2[2]);
|
|
if (unlikely(rval)) {
|
|
goto err_out_bad_array;
|
|
} else if (tags != ETAGAPQ) {
|
|
rval = -EFAULT;
|
|
goto err_out_bad_array;
|
|
}
|
|
ptr = (void __user *)e2k_ptr_ptr(descr.lo, descr.hi, 0);
|
|
iov_len = get_prot_msghdr_iovlen(ptr);
|
|
if (iov_len < 0) {
|
|
rval = iov_len;
|
|
goto err_out_bad_array;
|
|
}
|
|
/*
|
|
* Allocate space in user stack for args conversion.
|
|
* NB> We allocate extra field for final zero-element.
|
|
*/
|
|
args = get_user_space(((fields + 1) * sizeof(args[0])) +
|
|
sizeof(struct user_msghdr) + iov_len * sizeof(struct iovec));
|
|
/* Convert args array for socketcall from ptr */
|
|
rval = convert_array_3(a2, args, size, fields, 1,
|
|
mask_type, mask_align, 0,
|
|
CONV_ARR_WRONG_DSCR_FLD);
|
|
|
|
if (unlikely(rval))
|
|
goto err_out_bad_array;
|
|
|
|
/* Convert struct msghdr from args[1] */
|
|
prot_msghdr = (struct protected_user_msghdr __user *) args[1];
|
|
converted_msghdr = (struct user_msghdr __user *) (args + (fields + 1));
|
|
if (prot_msghdr) {
|
|
converted_msghdr = convert_msghdr(prot_msghdr,
|
|
SIZE_MSGHDR, "socketcall", "args[1]", converted_msghdr,
|
|
(a1 == SYS_SENDMSG) ? regs : NULL);
|
|
if (IS_ERR(converted_msghdr))
|
|
return PTR_ERR(converted_msghdr);
|
|
/* Set args[1] to pointer to converted structure */
|
|
args[1] = (unsigned long) converted_msghdr;
|
|
} else {
|
|
args[1] = 0;
|
|
DbgSCP("Empty user_msghdr in args[1]\n");
|
|
}
|
|
/* Other socketcalls */
|
|
} else {
|
|
if (fields) {
|
|
/* Allocate space on user stack for args array */
|
|
args = get_user_space((fields + 1) * sizeof(args[0]));
|
|
/* Convert args array for socketcall from ptr */
|
|
rval = convert_array(a2, args, size,
|
|
fields, 1, mask_type,
|
|
mask_align);
|
|
if (rval)
|
|
goto err_out_bad_array;
|
|
} else {
|
|
DbgSCP("Using args as is; convert_array not called.\n");
|
|
args = (unsigned long __user *) a2;
|
|
}
|
|
}
|
|
|
|
/* Calling regular socketcall function with converted arguments: */
|
|
rval = sys_socketcall((int) a1, args);
|
|
|
|
if (!rval && (a1 == SYS_RECVMSG)) {
|
|
long ret;
|
|
/* Updating the msg_flags field @ user space: */
|
|
DbgSCP("Socket call RECVMSG returned msg_flags: 0x%x\n",
|
|
converted_msghdr->msg_flags);
|
|
rval = copy_in_user(&prot_msghdr->msg_flags, &converted_msghdr->msg_flags,
|
|
sizeof(converted_msghdr->msg_flags));
|
|
if (rval) {
|
|
PROTECTED_MODE_WARNING(PMSCWARN_SOCKETCALL_FAILED_TO_UPDATE_FLD,
|
|
"msg_flags");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
/* Updating the 'controllen' field @ user space: */
|
|
DbgSCP("Socket call RECVMSG returned 'controllen': %ld\n",
|
|
converted_msghdr->msg_controllen);
|
|
ret = copy_in_user(&prot_msghdr->msg_controllen, &converted_msghdr->msg_controllen,
|
|
sizeof(converted_msghdr->msg_controllen));
|
|
if (ret) {
|
|
PROTECTED_MODE_WARNING(PMSCWARN_SOCKETCALL_FAILED_TO_UPDATE_FLD,
|
|
"controllen");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = ret;
|
|
}
|
|
}
|
|
|
|
DbgSCP(" (%d) returned %ld\n", (int) a1, rval);
|
|
return rval;
|
|
|
|
err_out_bad_array:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], "args", 2);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_sendmsg(const unsigned long sockfd,
|
|
const void __user *msg,
|
|
const unsigned long flags,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
long rval; /* syscall return value */
|
|
struct user_msghdr __user *converted_msghdr;
|
|
long iovlen;
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 1 /*min_size*/);
|
|
iovlen = get_prot_msghdr_iovlen(msg);
|
|
if (unlikely(iovlen < 0)) {
|
|
DbgSCP("get_prot_msghdr_iovlen(%ld) returned %ld\n", (long)msg, iovlen);
|
|
return iovlen;
|
|
}
|
|
converted_msghdr = get_user_space(sizeof(struct user_msghdr) +
|
|
iovlen * sizeof(struct iovec));
|
|
converted_msghdr = convert_msghdr(msg, size, "sendmsg", "msg", converted_msghdr, regs);
|
|
if (IS_ERR(converted_msghdr))
|
|
return PTR_ERR(converted_msghdr);
|
|
|
|
/* Call socketcall handler function: */
|
|
rval = sys_sendmsg(sockfd, converted_msghdr, flags);
|
|
|
|
DbgSCP(" returned %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_recvmsg(const unsigned long socket,
|
|
const void __user *message,
|
|
const unsigned long flags,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
long rval; /* syscall return value */
|
|
struct user_msghdr __user *converted_msghdr;
|
|
struct protected_user_msghdr __user *prot_msghdr =
|
|
(struct protected_user_msghdr __user *)message;
|
|
long iovlen;
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 1 /*min_size*/);
|
|
iovlen = get_prot_msghdr_iovlen(message);
|
|
if (unlikely(iovlen < 0)) {
|
|
DbgSCP("get_prot_msghdr_iovlen(%ld) returned %ld\n", (long)message, iovlen);
|
|
return iovlen;
|
|
}
|
|
converted_msghdr = get_user_space(sizeof(struct user_msghdr) +
|
|
iovlen * sizeof(struct iovec));
|
|
converted_msghdr = convert_msghdr(prot_msghdr, size, "recvmsg", "message",
|
|
converted_msghdr, NULL/*regs*/);
|
|
if (IS_ERR(converted_msghdr))
|
|
return PTR_ERR(converted_msghdr);
|
|
|
|
/* Call socketcall handler function: */
|
|
rval = sys_recvmsg(socket, converted_msghdr, flags);
|
|
DbgSCP("Syscall recvmsg(%ld, 0x%lx, 0x%lx) returned %ld\n",
|
|
socket, (long)converted_msghdr, flags, rval);
|
|
|
|
if (rval >= 0) {
|
|
long ret;
|
|
|
|
if (current->mm->context.pm_sc_debug_mode & PM_SC_DBG_MODE_COMPLEX_WRAPPERS) {
|
|
unsigned int ival;
|
|
unsigned long lval;
|
|
|
|
if (!get_user(ival, &converted_msghdr->msg_flags))
|
|
DbgSCP("Syscall recvmsg() returned msg_flags: 0x%x\n", ival);
|
|
if (!get_user(lval, &converted_msghdr->msg_controllen))
|
|
DbgSCP("Syscall recvmsg() returned 'controllen': %ld\n", lval);
|
|
|
|
}
|
|
/* Updating the 'msg_flags' field @ user space: */
|
|
ret = copy_in_user(&prot_msghdr->msg_flags, &converted_msghdr->msg_flags,
|
|
sizeof(prot_msghdr->msg_flags));
|
|
if (ret) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_FATAL_WRITE_AT_FIELD,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
&prot_msghdr->msg_flags, "user_msghdr->msg_flags");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = ret;
|
|
}
|
|
/* Updating the 'controllen' field @ user space: */
|
|
ret = copy_in_user(&prot_msghdr->msg_controllen, &converted_msghdr->msg_controllen,
|
|
sizeof(converted_msghdr->msg_controllen));
|
|
if (ret) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_FATAL_WRITE_AT_FIELD,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
&prot_msghdr->msg_controllen, "user_msghdr->msg_controllen");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = ret;
|
|
}
|
|
}
|
|
|
|
DbgSCP(" returned %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* Calculates total iovec buff number in the 'mmsghdr' structure.
|
|
* Returns: total iovec buff number in the 'mmsghdr' structure;
|
|
* or (-1) if size of 'mmsghdr' exceeds the one required by 'vlen';
|
|
* or negative error number to report.
|
|
*/
|
|
static long iovec_num_in_mmsghdr(const void __user *prot_mmsghdr,
|
|
const unsigned long prot_mmsghdr_size,
|
|
const unsigned int vlen)
|
|
{
|
|
void __user *msghdr128 = (void __user *)prot_mmsghdr;
|
|
long iov_len, iovec_num = 0;
|
|
long size = prot_mmsghdr_size;
|
|
int i;
|
|
|
|
for (i = 0; i < vlen; i++) {
|
|
iov_len = get_prot_msghdr_iovlen(msghdr128);
|
|
if (iov_len < 0)
|
|
return iov_len;
|
|
iovec_num += iov_len;
|
|
msghdr128 += sizeof(struct protected_mmsghdr);
|
|
size -= sizeof(struct protected_mmsghdr);
|
|
if (i < (vlen - 1) && size < sizeof(struct protected_mmsghdr))
|
|
return -1L;
|
|
}
|
|
|
|
return iovec_num;
|
|
}
|
|
|
|
#define MMSGHDR_STRUCT_SIZE_LONGS \
|
|
(sizeof(struct mmsghdr) / sizeof(long))
|
|
#define MMSGHDR_VECT_SIZE_LONGS(vlen) \
|
|
((sizeof(struct mmsghdr) * vlen) / sizeof(long))
|
|
|
|
static long convert_mmsghdr(void __user *prot_mmsghdr,
|
|
void __user *kernel_mmsghdr,
|
|
unsigned int size,
|
|
unsigned int vlen,
|
|
const char *syscall_name,
|
|
const struct pt_regs *regs)
|
|
/* Converts user msghdr structure from protected to regular structure format.
|
|
* Outputs: 0 if converted OK; error code otherwise.
|
|
* 'prot_msghdr' - protected message header structure.
|
|
* 'kernel_mmsghdr' - converted structure (to be allocated in syscall
|
|
* to avoid re-using stack area if allocated over here).
|
|
* 'size' - size of the input structure.
|
|
* 'vlen' - vector length if vector of structures is converted.
|
|
* 'syscall_name' - reference to particular syscall in diagnostic output.
|
|
*/
|
|
{
|
|
long __user *args = kernel_mmsghdr;
|
|
long __user *v_mmsrhdr;
|
|
struct mmsghdr __user *converted_mmsghdr;
|
|
struct user_msghdr __user *converted_msghdr;
|
|
long __user *converted_iovec;
|
|
int err, iov_len, i;
|
|
|
|
#define MASK_MMSGHDR_TYPE 0x0773 /* type mask for struct mmsghdr */
|
|
#define MASK_MMSGHDR_ALIGN 0xd7ff /* alignment mask for mmsghdr structure */
|
|
#define MASK_MMSGHDR_RW MASK_MSGHDR_RW
|
|
/*
|
|
* Structures user_msghdr and iovec contain pointers
|
|
* inside, therefore they need to be additionally
|
|
* converted with saving results in these structures
|
|
*/
|
|
|
|
/* (1) Converting 'mmsghdr' structure array: */
|
|
|
|
converted_mmsghdr = (struct mmsghdr __user *) args;
|
|
err = convert_array_3(prot_mmsghdr, (long __user *) converted_mmsghdr,
|
|
size, 8, vlen, MASK_MMSGHDR_TYPE,
|
|
MASK_MMSGHDR_ALIGN, MASK_MMSGHDR_RW,
|
|
CONV_ARR_WRONG_DSCR_FLD);
|
|
if (err) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_STRUCT_IN_ARG_NAME,
|
|
syscall_name, "mmsghdr", "msgvec");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* (2) Converting struct iovec fields in msghdr structures
|
|
* (msghdr->msg_iov):
|
|
*/
|
|
converted_iovec = args + MMSGHDR_VECT_SIZE_LONGS(vlen);
|
|
for (i = 0, v_mmsrhdr = args; i < vlen; i++) {
|
|
struct iovec __user *msg_iov;
|
|
|
|
converted_mmsghdr = (struct mmsghdr __user *) v_mmsrhdr;
|
|
converted_msghdr = &converted_mmsghdr->msg_hdr;
|
|
if (get_user(iov_len, &converted_msghdr->msg_iovlen)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_FROM,
|
|
__func__, (long) &converted_msghdr->msg_iovlen);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EFAULT);
|
|
return -EFAULT;
|
|
}
|
|
if (get_user(msg_iov, &converted_msghdr->msg_iov)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_FROM,
|
|
__func__, (long) &converted_msghdr->msg_iov);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EFAULT);
|
|
return -EFAULT;
|
|
}
|
|
if (msg_iov) {
|
|
err = convert_iov(msg_iov, converted_iovec, iov_len, regs);
|
|
if (err) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_FIELD_STRUCT_IN_ARG_NAME,
|
|
syscall_name, "iovec", "mmsghdr", "mmsghdr");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
} else {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_EMPTY_STRUCTURE_FIELD,
|
|
syscall_name, "msg_iov", "user_msghdr", "mmsghdr");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
converted_iovec = NULL;
|
|
}
|
|
|
|
/* Replacing iovec pointer in converted msghdr structure: */
|
|
if (put_user((void __user *) converted_iovec,
|
|
&converted_msghdr->msg_iov)) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_FATAL_WRITE_AT_FIELD,
|
|
syscall_name, &converted_msghdr->msg_iov, "msghdr->msg_iov");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EFAULT);
|
|
return -EFAULT;
|
|
}
|
|
|
|
v_mmsrhdr += MMSGHDR_STRUCT_SIZE_LONGS;
|
|
converted_iovec += iov_len * sizeof(struct iovec) / sizeof(long);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 1
|
|
#define print_mmsghdr_struct(a1, a2, a3)
|
|
#else
|
|
static void print_mmsghdr_struct(const char *title,
|
|
long __user *mmsghdr_arr,
|
|
const int vlen)
|
|
{
|
|
if (arch_init_pm_sc_debug_mode(PM_SC_DBG_MODE_CONV_STRUCT)) {
|
|
long __user *larr = mmsghdr_arr;
|
|
struct mmsghdr __user *mmsghdrp;
|
|
struct iovec __user *iovp;
|
|
long lval;
|
|
int i, j;
|
|
|
|
/* Print structure content: */
|
|
pr_info("%s[%d]:\n", title, vlen);
|
|
for (i = 0; i < vlen; i++) {
|
|
mmsghdrp = (struct mmsghdr *)larr;
|
|
pr_info("\t##### mmsghdr[%d] : 0x%lx #####\n",
|
|
i, (long)mmsghdrp);
|
|
for (j = 0; j < MMSGHDR_STRUCT_SIZE_LONGS; j++) {
|
|
pr_info("\t0x%.8x.%.8x\n",
|
|
(int)(*larr), (int)(*larr >> 32));
|
|
lar++;
|
|
}
|
|
iovp = mmsghdrp->msg_hdr.msg_iov;
|
|
for (j = 0; j < mmsghdrp->msg_hdr.msg_iovlen; j++) {
|
|
lval = (long) iovp;
|
|
pr_info("\t->msg_iov[%d: 0x%lx]: base = 0x%lx len = %ld\n",
|
|
j, lval,
|
|
(long)iovp->iov_base, iovp->iov_len);
|
|
lval += sizeof(struct iovec);
|
|
iovp = (struct iovec *)lval;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* print_mmsghdr_struct */
|
|
|
|
static long update_prot_mmsghdr_struct(long __user *mmsghdr_arr,
|
|
long __user *prot_msgvec,
|
|
const int vlen)
|
|
/* This is post-syscall post-processing procedure.
|
|
* Propagate .msg_len values from processed 'mmsghdr_arr' back to 'prot_msgvec'.
|
|
* 'vlen' - number of elements in the array.
|
|
* Returns error code or 0 if OK.
|
|
*/
|
|
{
|
|
#define MMSGHDR_STR_LEN_OFFSET 96
|
|
/* .msg_len field offset in the protected structure */
|
|
#define PROT_MMSGHDR_SIZE sizeof(struct protected_mmsghdr)
|
|
/* size of struct mmsghdr in prot. user space */
|
|
long __user *from = mmsghdr_arr;
|
|
long __user *to = prot_msgvec;
|
|
struct mmsghdr __user *mmsghdr_from;
|
|
long val;
|
|
int i;
|
|
|
|
to += MMSGHDR_STR_LEN_OFFSET / sizeof(long);
|
|
|
|
for (i = 0; i < vlen; i++) {
|
|
mmsghdr_from = (struct mmsghdr __user *) from;
|
|
if (get_user(val, &mmsghdr_from->msg_len)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_FROM,
|
|
__func__, (long) &mmsghdr_from->msg_len);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EFAULT);
|
|
return -EFAULT;
|
|
}
|
|
DbgSCP("mmsghdr[%d].msg_len = %ld\n", i, val);
|
|
if (put_user(val, to)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_WRITE_AT,
|
|
__func__, (long) to);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EFAULT);
|
|
return -EFAULT;
|
|
}
|
|
|
|
from += MMSGHDR_STRUCT_SIZE_LONGS;
|
|
to += PROT_MMSGHDR_SIZE / sizeof(long);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define PROTECTED_MMSGHDR_SIZE(vlen) \
|
|
(PROT_MMSGHDR_SIZE * vlen)
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_sendmmsg(const unsigned long sockfd,
|
|
void __user *msgvec,
|
|
const unsigned long vlen, /* vector lngth */
|
|
const unsigned long flags,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
long rval; /* syscall return value */
|
|
long iov_total_num;
|
|
void __user *kernel_mmsghdr;
|
|
|
|
DbgSCP(" sockfd=%ld vlen=%ld\n", sockfd, vlen);
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EINVAL;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 1 /*min_size*/);
|
|
if (size < PROTECTED_MMSGHDR_SIZE(vlen)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_MISMATCHES_FIELD_VAL,
|
|
"sendmmsg", "msgvec", size, "vlen", PROTECTED_MMSGHDR_SIZE(vlen));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iov_total_num = iovec_num_in_mmsghdr(msgvec, size, vlen);
|
|
if (unlikely(iov_total_num == -1)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_MISMATCHES_FIELD_VAL,
|
|
"sendmmsg", "msgvec", size, "vlen", PROTECTED_MMSGHDR_SIZE(vlen));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
} else if (unlikely(iov_total_num < 0)) {
|
|
return iov_total_num;
|
|
}
|
|
/* NB> For the sake of performance we don't calculate exact vector size.
|
|
* Instead, we allocate same space as in PM, which is bigger
|
|
* and is quite enough for kernel structire for sure.
|
|
*/
|
|
kernel_mmsghdr = get_user_space(size + (iov_total_num * sizeof(struct iovec)));
|
|
if (!kernel_mmsghdr) {
|
|
pr_err("%s:%d : FATAL ERROR: failed to allocate %d bytes on stack !!!",
|
|
__FILE__, __LINE__, size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (convert_mmsghdr(msgvec, kernel_mmsghdr, size, vlen, "sendmmsg", regs))
|
|
return -EINVAL;
|
|
|
|
if (arch_init_pm_sc_debug_mode(PM_SC_DBG_MODE_CONV_STRUCT))
|
|
print_mmsghdr_struct("protected sendmmsg: converted mmsghdr",
|
|
kernel_mmsghdr, vlen);
|
|
|
|
rval = sys_sendmmsg(sockfd, kernel_mmsghdr, vlen, flags);
|
|
if (rval <= 0)
|
|
DbgSCP("sys_sendmmsg() failed with error code %ld\n", rval);
|
|
|
|
if (arch_init_pm_sc_debug_mode(PM_SC_DBG_MODE_CONV_STRUCT))
|
|
print_mmsghdr_struct("protected sendmmsg: post-syscall mmsghdr",
|
|
kernel_mmsghdr, vlen);
|
|
|
|
if (rval > 0) {
|
|
/* Propagating .msg_len values back to 'msgvec' */
|
|
long ret;
|
|
|
|
ret = update_prot_mmsghdr_struct(kernel_mmsghdr, msgvec, (int) vlen);
|
|
if (ret)
|
|
rval = ret;
|
|
}
|
|
|
|
DbgSCP(" returned %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_recvmmsg(const unsigned long sockfd,
|
|
const unsigned long __user msgvec,
|
|
const unsigned long vlen, /* vector lngth */
|
|
const unsigned long flags,
|
|
const unsigned long __user timeout,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
long rval; /* syscall return value */
|
|
long iov_total_num;
|
|
long __user *kernel_mmsghdr;
|
|
|
|
DbgSCP(" sockfd=%ld vlen=%ld\n", sockfd, vlen);
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 1 /*min_size*/);
|
|
if (size < PROTECTED_MMSGHDR_SIZE(vlen)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_MISMATCHES_FIELD_VAL,
|
|
"recvmmsg", "msgvec", size, "vlen", PROTECTED_MMSGHDR_SIZE(vlen));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iov_total_num = iovec_num_in_mmsghdr((void __user *)msgvec, size, vlen);
|
|
if (unlikely(iov_total_num == -1)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_MISMATCHES_FIELD_VAL,
|
|
"sendmmsg", "msgvec", size, "vlen", PROTECTED_MMSGHDR_SIZE(vlen));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
} else if (unlikely(iov_total_num < 0)) {
|
|
return iov_total_num;
|
|
}
|
|
|
|
/* NB> For the sake of performance we allocate same space as in PM. */
|
|
kernel_mmsghdr = get_user_space(size + (iov_total_num * sizeof(struct iovec)));
|
|
if (!kernel_mmsghdr) {
|
|
pr_err("%s:%d : FATAL ERROR: failed to allocate %d bytes on stack !!!",
|
|
__FILE__, __LINE__, size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (convert_mmsghdr((long __user *) msgvec, kernel_mmsghdr,
|
|
size, vlen, "recvmmsg", NULL))
|
|
return -EINVAL;
|
|
|
|
if (arch_init_pm_sc_debug_mode(PM_SC_DBG_MODE_CONV_STRUCT))
|
|
print_mmsghdr_struct("protected recvmmsg: converted mmsghdr",
|
|
kernel_mmsghdr, vlen);
|
|
|
|
rval = sys_recvmmsg(sockfd, (struct mmsghdr __user *) kernel_mmsghdr, vlen,
|
|
flags, (struct __kernel_timespec __user *) timeout);
|
|
|
|
if (rval <= 0) {
|
|
DbgSCP("sys_recvmmsg() failed with error code %ld\n", rval);
|
|
} else { /* (rval > 0) */
|
|
long ret;
|
|
|
|
ret = update_prot_mmsghdr_struct(kernel_mmsghdr,
|
|
(long __user *)msgvec, (int)vlen);
|
|
if (ret)
|
|
rval = ret;
|
|
}
|
|
|
|
DbgSCP(" returned %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
|
|
/*
|
|
* Selecting proper convert_array masks (type and align) and argument number
|
|
* to convert protected array of arguments to the corresponding sys_ipc syscall.
|
|
* NB> Elements of the array are normally of types long and descriptor.
|
|
*/
|
|
notrace __section(".entry.text")
|
|
static inline void get_ipc_mask(long call, long *mask_type, long *mask_align,
|
|
int *fields)
|
|
{
|
|
/* According to sys_ipc () these are SEMTIMEDOP and (MSGRCV |
|
|
* (1 << 16))' (see below on why MSGRCV is not useful in PM) calls that
|
|
* make use of FIFTH argument. Both of them interpret it as a long. Thus
|
|
* all other calls may be considered as 4-argument ones. Some of them
|
|
* may accept less than 4 arguments.
|
|
*/
|
|
switch (call) {
|
|
case (MSGRCV | (1 << 16)):
|
|
/* Instead it's much more handy to pass MSGP as PTR (aka FOURTH)
|
|
* and MSGTYP as FIFTH. `1 << 16' makes it clear to `sys_ipc ()'
|
|
* that this way of passing arguments is used.
|
|
*/
|
|
case SEMTIMEDOP:
|
|
*mask_type = 0x3d5;
|
|
*mask_align = 0x3f5;
|
|
*fields = 5;
|
|
break;
|
|
case SHMAT:
|
|
/* SHMAT is special because it interprets the THIRD argument as
|
|
* a pointer to which AP should be stored in PM.
|
|
*/
|
|
*mask_type = 0xf5;
|
|
*mask_align = 0xfd;
|
|
*fields = 3;
|
|
break;
|
|
case SEMGET:
|
|
case SHMGET:
|
|
*mask_type = 0x15;
|
|
*mask_align = 0x15;
|
|
*fields = 3;
|
|
break;
|
|
case MSGGET:
|
|
*mask_type = 0x5;
|
|
*mask_align = 0x5;
|
|
*fields = 2;
|
|
break;
|
|
default:
|
|
*mask_type = 0xd5;
|
|
*mask_align = 0xf5;
|
|
*fields = 4;
|
|
DbgSCP("default ipc masks used in the ipc call %ld\n", call);
|
|
}
|
|
DbgSCP("call=%ld mask_type=0x%lx mask_align=0x%lx fields=%d\n",
|
|
call, *mask_type, *mask_align, *fields);
|
|
}
|
|
|
|
static long process_shmat_syscall_result(const int shmid, const int shmflg,
|
|
ulong __user *raddr)
|
|
{
|
|
/* This is 'shmat' syscall post-processing for protected execution mode.
|
|
* We need to convert obtained shm pointer to descriptor
|
|
* (must have been available in *raddr) and pass it to 'raddr':
|
|
*/
|
|
unsigned long segm_size;
|
|
ulong base;
|
|
e2k_ptr_t dscr;
|
|
int access;
|
|
long rval; /* return value */
|
|
|
|
/* taking shm parameters from shmid: */
|
|
segm_size = get_shm_segm_size(shmid);
|
|
DbgSCP("(%d): segm_size = %ld\n", shmid, segm_size);
|
|
|
|
if (IS_ERR_VALUE(segm_size))
|
|
return (long) segm_size;
|
|
|
|
access = (shmflg & SHM_RDONLY) ? R_ENABLE : RW_ENABLE;
|
|
|
|
if (get_user(base, raddr))
|
|
return -EFAULT;
|
|
|
|
dscr.lo = make_ap_lo(base, segm_size, 0, access);
|
|
dscr.hi = make_ap_hi(base, segm_size, 0, access);
|
|
|
|
DbgSCP("(%d): lo = 0x%llx hi = 0x%llx\n", shmid, dscr.lo, dscr.hi);
|
|
|
|
rval = put_user_tagged_16(dscr.lo, dscr.hi, ETAGAPQ, raddr);
|
|
if (rval)
|
|
rval = -EFAULT;
|
|
|
|
DbgSCP("(%d) returned %ld\n", shmid, rval);
|
|
return rval;
|
|
}
|
|
|
|
static inline
|
|
int prot_arg_is_ap(const struct pt_regs *regs,
|
|
int arg_num) /* argument # in syscall */
|
|
/* Checks that argument #argnum is descriptor: */
|
|
{
|
|
int tag = (regs->tags >> (arg_num * 8)) & 0xff;
|
|
|
|
return (tag == ETAGAPQ);
|
|
}
|
|
|
|
static inline
|
|
int prot_arg_is_int(const struct pt_regs *regs,
|
|
int arg_num) /* argument # in syscall */
|
|
/* Checks that argument #argnum is of type 'int': */
|
|
{
|
|
int tag = (regs->tags >> (arg_num * 8)) & 0xff;
|
|
|
|
return ((tag & 3) == 0);
|
|
}
|
|
|
|
static inline
|
|
int check_prot_semun_struct(const struct pt_regs *regs,
|
|
int arg_num, /* argument # in syscall */
|
|
size_t size) /* min size of descriptor */
|
|
/* Returns: 1 - if check passed OK; 0 - otherwise */
|
|
{
|
|
if (!regs->args[arg_num * 2 - 1]) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], "semun", arg_num);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return 0;
|
|
}
|
|
/* Check that union semun arg #arg_num contains proper pointer: */
|
|
if (!prot_arg_is_ap(regs, arg_num)) {
|
|
unsigned long ptr = e2k_ptr_ptr(regs->args[arg_num * 2 - 1],
|
|
regs->args[arg_num * 2], 0);
|
|
|
|
PROTECTED_MODE_ALERT(PMCNVSTRMSG_STRUCT_DOESNT_CONTAIN_DESCR,
|
|
"semun", ptr);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return 0;
|
|
}
|
|
if (size) {
|
|
int dscr_size;
|
|
|
|
dscr_size = e2k_ptr_size(regs->args[arg_num * 2 - 1],
|
|
regs->args[arg_num * 2], 0);
|
|
if (dscr_size < size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"semun", dscr_size, size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_semctl(const long semid, /* a1 */
|
|
const long semnum, /* a2 */
|
|
const long cmd, /* a3 */
|
|
void __user *ptr, /* a4 */
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
void __user *fourth = NULL; /* fourth arg to 'semctl' syscall */
|
|
long rval; /* syscall return value */
|
|
|
|
if (semid < 0) {
|
|
rval = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Fields of union semun depend on the 'cmd' parameter */
|
|
switch (cmd & ~IPC_64) {
|
|
/* Pointer in union semun required */
|
|
case IPC_STAT:
|
|
case IPC_SET:
|
|
case SEM_STAT:
|
|
case SEM_STAT_ANY:
|
|
if (!check_prot_semun_struct(regs, 4/*arg#*/, sizeof(struct semid_ds))) {
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
case SETALL:
|
|
case GETALL:
|
|
if (!check_prot_semun_struct(regs, 4/*arg#*/, 0/*size*/)) {
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
case IPC_INFO:
|
|
case SEM_INFO:
|
|
if (!check_prot_semun_struct(regs, 4/*arg#*/, sizeof(struct seminfo))) {
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
/* Int value in union semun required */
|
|
case SETVAL:
|
|
if (!prot_arg_is_int(regs, 4)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_UNEXP_ARG_TAG_ID,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
(regs->tags >> (4 * 8)) & 0xff/*tag*/, 4/*arg#*/);
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
/* No 'semun' argument */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rval = sys_old_semctl((int) semid, (int) semnum, (int) cmd, (unsigned long) fourth);
|
|
out:
|
|
DbgSCP("(cmd=%d, semnum=%d, semun=0x%lx) returned %ld\n",
|
|
(int) cmd, (int) semnum, fourth, rval);
|
|
return rval;
|
|
}
|
|
|
|
/* long sys_shmat(int shmid, char __user *shmaddr, int shmflg); */
|
|
notrace __section(".entry.text")
|
|
long protected_sys_shmat(const long shmid, /* a1 */
|
|
const unsigned long __user shmaddr, /* a2 */
|
|
const long shmflg, /* a3 */
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
struct pt_regs *regs)
|
|
{
|
|
unsigned long segm_size;
|
|
ulong base;
|
|
unsigned long lo = 0, hi = 0;
|
|
int access;
|
|
int rv1_tag = E2K_NUMERIC_ETAG, rv2_tag = E2K_NUMERIC_ETAG;
|
|
long rval; /* syscall return value */
|
|
|
|
rval = sys_shmat((int) shmid, (char __user *) shmaddr, (int) shmflg);
|
|
|
|
if (IS_ERR_VALUE(rval))
|
|
goto err_out;
|
|
base = (ulong) rval;
|
|
|
|
/*
|
|
* 'shmat' syscall post-processing for protected execution mode:
|
|
* We need to convert obtained shm pointer to descriptor
|
|
*/
|
|
|
|
segm_size = get_shm_segm_size(shmid);
|
|
DbgSCP("(%ld): segm_size = %ld\n", shmid, segm_size);
|
|
|
|
if (IS_ERR_VALUE(segm_size)) {
|
|
rval = (long) segm_size;
|
|
goto err_out;
|
|
}
|
|
|
|
access = (shmflg & SHM_RDONLY) ? R_ENABLE : RW_ENABLE;
|
|
|
|
lo = make_ap_lo(base, segm_size, 0, access);
|
|
hi = make_ap_hi(base, segm_size, 0, access);
|
|
|
|
rv1_tag = E2K_AP_LO_ETAG;
|
|
rv2_tag = E2K_AP_HI_ETAG;
|
|
rval = 0;
|
|
err_out:
|
|
regs->return_desk = 1;
|
|
regs->rval1 = lo;
|
|
regs->rval2 = hi;
|
|
regs->rv1_tag = rv1_tag;
|
|
regs->rv2_tag = rv2_tag;
|
|
DbgSCP("rval = %ld (hex: %lx) - 0x%lx : 0x%lx t1/t2=0x%x/0x%x\n",
|
|
rval, rval, lo, hi, rv1_tag, rv2_tag);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_ipc(const unsigned long call, /* a1 */
|
|
long first, /* a2 */
|
|
unsigned long second, /* a3 */
|
|
unsigned long third, /* a4 */
|
|
void __user *ptr, /* a5 */
|
|
long fifth, /* a6 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
long mask_type, mask_align;
|
|
int fields;
|
|
void __user *fourth = ptr; /* fourth arg to 'ipc' syscall */
|
|
long rval; /* syscall return value */
|
|
|
|
get_ipc_mask(call, &mask_type, &mask_align, &fields);
|
|
if ((fields == 0) || (unlikely(fields > 5))) {
|
|
pr_err("%s:%d : Bad syscall_ipc field number %ld\n",
|
|
__FILE__, __LINE__, call);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Syscalls that follow require converting arg-structures: */
|
|
switch (call) {
|
|
case SEMCTL: {
|
|
/* NB> Union semun (5-th argument) contains pointer.
|
|
* Effective field of union semun depends on cmd parameter:
|
|
*/
|
|
switch (third & ~IPC_64) {
|
|
/* Pointer in union semun required */
|
|
case IPC_STAT:
|
|
case IPC_SET:
|
|
case SEM_STAT:
|
|
case SEM_STAT_ANY:
|
|
if (!check_prot_semun_struct(regs, 5/*arg#*/,
|
|
sizeof(struct semid_ds))) {
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
case SETALL:
|
|
case GETALL:
|
|
if (!check_prot_semun_struct(regs, 5/*arg#*/, 0/*size*/)) {
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
case IPC_INFO:
|
|
case SEM_INFO:
|
|
if (!check_prot_semun_struct(regs, 5/*arg#*/,
|
|
sizeof(struct seminfo))) {
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
/* Int value in union semun required */
|
|
case SETVAL:
|
|
if (!prot_arg_is_int(regs, 5)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_UNEXP_ARG_TAG_ID,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
(regs->tags >> (5 * 8)) & 0xff/*tag*/, 5/*arg#*/);
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
fourth = ptr;
|
|
break;
|
|
/* No union semun as argument */
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case MSGRCV: {
|
|
#define MASK_MSG_BUF_PTR_TYPE 0x7 /* type mask for struct msg_buf */
|
|
#define MASK_MSG_BUF_PTR_ALIGN 0x7 /* alignment mask for struct msg_buf */
|
|
#define SIZE_MSG_BUF_PTR 32 /* size of struct msg_buf with pointer */
|
|
/*
|
|
* NB> Library uses different msg structure,
|
|
* not the one sys_msgrcv syscall uses.
|
|
* Struct new_msg_buf (ipc_kludge) contains pointer
|
|
* inside, therefore it needs to be additionally
|
|
* converted with saving results in these struct
|
|
*/
|
|
struct ipc_kludge __user *converted_new_msg_buf;
|
|
|
|
converted_new_msg_buf =
|
|
get_user_space(sizeof(struct ipc_kludge));
|
|
rval = convert_array(ptr, converted_new_msg_buf,
|
|
SIZE_MSG_BUF_PTR, 2, 1,
|
|
MASK_MSG_BUF_PTR_TYPE,
|
|
MASK_MSG_BUF_PTR_ALIGN);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_WRONG_ARG_VALUE_LX,
|
|
"ipc(MSGRCV, ...)", "ptr", (long) ptr);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Assign args[3] to pointer to converted new_msg_buf
|
|
*/
|
|
fourth = converted_new_msg_buf;
|
|
break;
|
|
}
|
|
default: /* other options don't require extra arg processing */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Call syscall_ipc handler function with passing of
|
|
* arguments to it
|
|
*/
|
|
|
|
DbgSCP(" call:%d 1st:0x%x 2nd:0x%lx 3rd:0x%lx\nptr:%p 5th:0x%lx\n",
|
|
(u32) call, (int) first, (unsigned long) second,
|
|
(unsigned long) third, fourth, fifth);
|
|
|
|
rval = sys_ipc((u32) call, (int) first, (unsigned long) second,
|
|
(unsigned long) third, fourth, fifth);
|
|
|
|
if (!IS_ERR_VALUE(rval) && (call == SHMAT)) {
|
|
/* we need to return descriptor to pointer in args[1] */
|
|
rval = process_shmat_syscall_result(
|
|
(int) first /*shmid*/,
|
|
(int) second /*shmflg*/,
|
|
(ulong __user *) third /**raddr*/);
|
|
}
|
|
out:
|
|
DbgSCP("(%d) returned %ld\n", (int) call, rval);
|
|
return rval;
|
|
}
|
|
|
|
__section(".entry.text")
|
|
static long prot_sys_mmap(const unsigned long start,
|
|
const unsigned long length, const unsigned long prot,
|
|
const unsigned long flags, const unsigned long fd,
|
|
const unsigned long offset, const int offset_in_bytes,
|
|
struct pt_regs *regs)
|
|
{
|
|
long rval = -EINVAL; /* syscall return value */
|
|
e2k_addr_t base;
|
|
long rval1 = 0, rval2 = 0;
|
|
int rv1_tag = E2K_NUMERIC_ETAG, rv2_tag = E2K_NUMERIC_ETAG;
|
|
|
|
DbgSCP("start = %ld, len = %ld (0x%lx), prot = 0x%lx ", start, length, length, prot);
|
|
DbgSCP("flags = 0x%lx, fd = 0x%lx, off = %ld, in_bytes=%d",
|
|
flags, fd, offset, offset_in_bytes);
|
|
if (!length)
|
|
goto nr_mmap_out;
|
|
|
|
if ((length > 0) && (length >> 31)) {
|
|
/* NB> For details on this limitation see bug #99875 */
|
|
PROTECTED_MODE_ALERT(PMMMAPMSG_ATTEMPT_TO_MAP_BYTES,
|
|
"mmap()", length, length);
|
|
PROTECTED_MODE_MESSAGE(0, PMMMAPMSG_CANT_MAP_OVER_2GB);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
/* NB> We cannot simply return error code as
|
|
* this syscall returns structured result.
|
|
*/
|
|
goto nr_mmap_out;
|
|
}
|
|
if (offset < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_NEGATIVE_SIZE_VALUE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
offset, 2/*argnum*/);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, 0, EINVAL);
|
|
goto nr_mmap_out;
|
|
}
|
|
if (size_exceeds_descr_max_capacity((offset_in_bytes ? offset : (offset * PAGE_SIZE)),
|
|
"offset", offset, regs)) {
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, 0, EINVAL);
|
|
goto nr_mmap_out;
|
|
}
|
|
|
|
if (offset_in_bytes)
|
|
base = sys_mmap((unsigned long) start, (unsigned long) length,
|
|
(unsigned long) prot, (unsigned long) flags,
|
|
(unsigned long) fd, (unsigned long) offset);
|
|
else /* this is __NR_mmap2 */
|
|
base = sys_mmap2((unsigned long) start, (unsigned long) length,
|
|
(unsigned long) prot, (unsigned long) flags,
|
|
(unsigned long) fd, (unsigned long) offset);
|
|
DbgSCP("base = 0x%lx\n", (unsigned long)base);
|
|
if (base & ~PAGE_MASK) { /* this is error code */
|
|
rval = base;
|
|
goto nr_mmap_out;
|
|
}
|
|
|
|
rval1 = make_ap_lo(base, length, 0, RW_ENABLE);
|
|
rval2 = make_ap_hi(base, length, 0, RW_ENABLE);
|
|
rv1_tag = E2K_AP_LO_ETAG;
|
|
rv2_tag = E2K_AP_HI_ETAG;
|
|
rval = 0;
|
|
nr_mmap_out:
|
|
regs->return_desk = 1;
|
|
regs->rval1 = rval1;
|
|
regs->rval2 = rval2;
|
|
regs->rv1_tag = rv1_tag;
|
|
regs->rv2_tag = rv2_tag;
|
|
DbgSCP("rval = %ld (0x%lx) dscr = 0x%lx : 0x%x.%.8x t1/t2=0x%x/0x%x\n",
|
|
rval, rval, rval1, (u32)(rval2 >> 32), (u32)rval2, rv1_tag, rv2_tag);
|
|
return rval;
|
|
}
|
|
|
|
__section(".entry.text")
|
|
long protected_sys_mmap(const unsigned long a1, /* start */
|
|
const unsigned long a2, /* length */
|
|
const unsigned long a3, /* prot */
|
|
const unsigned long a4, /* flags */
|
|
const unsigned long a5, /* fd */
|
|
const unsigned long a6, /* offset */
|
|
struct pt_regs *regs)
|
|
{
|
|
if (a2 < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_NEGATIVE_SIZE_VALUE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], a2, 2/*argnum*/);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, 0, EINVAL);
|
|
}
|
|
if (a6 < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_NEGATIVE_SIZE_VALUE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], a6, 6/*argnum*/);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, 0, EINVAL);
|
|
}
|
|
return prot_sys_mmap(a1, a2, a3, a4, a5, a6, 1, regs);
|
|
}
|
|
|
|
__section(".entry.text")
|
|
long protected_sys_mmap2(const unsigned long a1, /* start */
|
|
const unsigned long a2, /* length */
|
|
const unsigned long a3, /* prot */
|
|
const unsigned long a4, /* flags */
|
|
const unsigned long a5, /* fd */
|
|
const unsigned long a6, /* offset */
|
|
struct pt_regs *regs)
|
|
{
|
|
if (a2 < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_NEGATIVE_SIZE_VALUE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], a2, 2/*argnum*/);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, 0, EINVAL);
|
|
}
|
|
if (a6 < 0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_NEGATIVE_SIZE_VALUE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], a6, 6/*argnum*/);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, 0, EINVAL);
|
|
}
|
|
return prot_sys_mmap(a1, a2, a3, a4, a5, a6, 0, regs);
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_unuselib(const unsigned long __user a1, /* address of module */
|
|
const unsigned long unused2,
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
struct pt_regs *regs)
|
|
{
|
|
unsigned long rval;
|
|
/* Base address of module data segment */
|
|
unsigned long glob_base = a1;
|
|
/* Size of module data segment */
|
|
size_t glob_size;
|
|
|
|
if (warn_if_not_descr(1, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
glob_size = e2k_ptr_size(regs->args[1], regs->args[2],
|
|
1 /*min_size*/);
|
|
|
|
/* Unload module from memory */
|
|
if (current->thread.flags & E2K_FLAG_3P_ELF32)
|
|
rval = sys_unload_cu_elf32_3P(glob_base,
|
|
glob_size);
|
|
else
|
|
rval = sys_unload_cu_elf64_3P(glob_base,
|
|
glob_size);
|
|
|
|
if (rval) {
|
|
DbgSCP("failed, could not unload module with"
|
|
" data_base = 0x%lx , data_size = 0x%lx\n",
|
|
glob_base, glob_size);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_munmap(const unsigned long addr, /* a1 */
|
|
unsigned long length, /* a2 */
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
struct pt_regs *regs)
|
|
{
|
|
long rval; /* syscall return value */
|
|
|
|
DbgSCP("(addr=%lx, len=%lx) ", addr, length);
|
|
|
|
if (!addr || !length)
|
|
return -EINVAL;
|
|
|
|
if (warn_if_not_descr(1, CHECK4DESCR_SILENT, regs))
|
|
return -EINVAL;
|
|
|
|
/* NB> Value of 'length' is controlled by the size correction bit in the syscall mask. */
|
|
|
|
if (e2k_ptr_itag(regs->args[1]) != AP_ITAG) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_DESCR_IN_STACK,
|
|
sys_call_ID_to_name[regs->sys_num], addr, "addr");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_munmap(addr, length);
|
|
DbgSCP("rval = %ld (hex: %lx)\n", rval, rval);
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_get_backtrace(const unsigned long __user buf, /* a1 */
|
|
size_t count, size_t skip, /* a2,3 */
|
|
unsigned long flags, /* a4 */
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
|
|
DbgSCP("(buf=0x%lx, count=%ld, skip=%ld, flags=0x%lx)\n",
|
|
buf, count, skip, flags);
|
|
size = e2k_ptr_size(regs->args[1], regs->args[2], 0);
|
|
if (size < (count * 8)) {
|
|
if (!size_exceeds_descr_max_capacity((count * 8), "count", count, regs))
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_COUNT_EXCEEDS_DESCR_SIZE,
|
|
regs->sys_num, "get_backtrace", (count * 8), size, 1);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
return sys_get_backtrace((unsigned long *) buf, count, skip, flags);
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_set_backtrace(const unsigned long __user buf, /* a1 */
|
|
size_t count, size_t skip, /* a2,3 */
|
|
unsigned long flags, /* a4 */
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
|
|
DbgSCP("(buf=0x%lx, count=%ld, skip=%ld, flags=0x%lx)\n",
|
|
buf, count, skip, flags);
|
|
size = e2k_ptr_size(regs->args[1], regs->args[2], 0);
|
|
if (size < (count * 8)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_COUNT_EXCEEDS_DESCR_SIZE, regs->sys_num,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
(count * 8), size, 1);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
return sys_set_backtrace((unsigned long *) buf, count, skip, flags);
|
|
}
|
|
|
|
struct prot_robust_list {
|
|
e2k_ptr_t next;
|
|
};
|
|
|
|
struct prot_robust_list_head {
|
|
struct prot_robust_list list;
|
|
long futex_offset;
|
|
e2k_ptr_t list_op_pending;
|
|
} prot_robust_list_head_t;
|
|
|
|
|
|
#define SIZEOF_PROT_HEAD_STRUCT (sizeof(prot_robust_list_head_t))
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_set_robust_list(const unsigned long __user listhead, /* a1 */
|
|
const size_t len, /* a2 */
|
|
const unsigned long unused3,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
DbgSCP("(head=0x%lx, len=%zd)\n", listhead, len);
|
|
|
|
if (!futex_cmpxchg_enabled) {
|
|
DbgSCP("futex_cmpxchg is not enabled\n");
|
|
return -ENOSYS;
|
|
}
|
|
|
|
if (unlikely(len != SIZEOF_PROT_HEAD_STRUCT)) {
|
|
if ((long)len < 0)
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NEGATIVE_SIZE_VALUE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
(long)len, 2);
|
|
else
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_DIFFERS_STRUCT_SIZE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"len", len, "robust_list_head",
|
|
SIZEOF_PROT_HEAD_STRUCT);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!e2k_ptr_size(regs->args[1], regs->args[2], SIZEOF_PROT_HEAD_STRUCT)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_DIFFERS_STRUCT_SIZE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"dsk_len", e2k_ptr_size(regs->args[1], regs->args[2], 0),
|
|
"robust_list_head", SIZEOF_PROT_HEAD_STRUCT);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
current_thread_info()->pm_robust_list.lo = regs->args[1];
|
|
current_thread_info()->pm_robust_list.hi = regs->args[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_get_robust_list(const unsigned long pid,
|
|
e2k_ptr_t __user *head_ptr, size_t __user *len_ptr)
|
|
{
|
|
unsigned long ret; /* result of the function */
|
|
struct task_struct *p;
|
|
e2k_ptr_t dscr;
|
|
int len;
|
|
|
|
DbgSCP("(pid=%ld, head_ptr=0x%lx, len_ptr=0x%lx)\n",
|
|
pid, head_ptr, len_ptr);
|
|
|
|
if (!futex_cmpxchg_enabled) {
|
|
DbgSCP("futex_cmpxchg is not enabled\n");
|
|
return -ENOSYS;
|
|
}
|
|
|
|
rcu_read_lock();
|
|
|
|
ret = -ESRCH;
|
|
if (!pid) {
|
|
p = current;
|
|
} else {
|
|
p = find_task_by_vpid(pid);
|
|
if (!p)
|
|
goto err_unlock;
|
|
}
|
|
|
|
ret = -EPERM;
|
|
if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
|
|
goto err_unlock;
|
|
|
|
dscr = task_thread_info(p)->pm_robust_list;
|
|
rcu_read_unlock();
|
|
|
|
if (!dscr.lo) {
|
|
DbgSCP("robust_list is not set yet\n");
|
|
len = sizeof(dscr);
|
|
memset(&dscr, 0, len);
|
|
ret = 0;
|
|
goto empty_list_out;
|
|
}
|
|
|
|
len = e2k_ptr_size(dscr.lo, dscr.hi, 0);
|
|
DbgSCP("list head stored: lo=0x%llx hi=0x%llx len=%d\n",
|
|
dscr.lo, dscr.hi, len);
|
|
if (unlikely(len < SIZEOF_PROT_HEAD_STRUCT)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE, __func__,
|
|
"robust_list_head", len,
|
|
(size_t) SIZEOF_PROT_HEAD_STRUCT);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EFAULT;
|
|
}
|
|
|
|
DbgSCP("robust_list head: lo=0x%llx hi=0x%llx len=%d\n",
|
|
dscr.lo, dscr.hi, len);
|
|
|
|
len = SIZEOF_PROT_HEAD_STRUCT;
|
|
empty_list_out:
|
|
if (put_user_tagged_16(dscr.lo, dscr.hi, ETAGAPQ, head_ptr) ||
|
|
put_user(len, len_ptr))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
err_unlock:
|
|
rcu_read_unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
static
|
|
long protected_sys_process_vm_readwritev(const unsigned long pid,
|
|
const struct prot_iovec __user *lvec,
|
|
unsigned long liovcnt,
|
|
const struct prot_iovec __user *rvec,
|
|
unsigned long riovcnt,
|
|
unsigned long flags,
|
|
const struct pt_regs *regs,
|
|
const int vm_write)
|
|
{
|
|
pid_t id = pid;
|
|
int lsize, rsize;
|
|
struct iovec __user *lv = NULL;
|
|
struct iovec __user *rv = NULL;
|
|
long rval;
|
|
|
|
DbgSCP("(%ld, lvec=0x%lx, lcnt=%ld, rvec=0x%lx, rcnt=%ld, flg=0x%lx)\n",
|
|
pid, lvec, liovcnt, rvec, riovcnt, flags);
|
|
|
|
lsize = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (lsize < (sizeof(struct iovec) * liovcnt)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_MISMATCHES_FIELD_VAL,
|
|
sys_call_ID_to_name[regs->sys_num], "lvec", lsize,
|
|
"liovcnt", sizeof(struct iovec) * liovcnt);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
return -EFAULT;
|
|
}
|
|
rsize = e2k_ptr_size(regs->args[7], regs->args[8], 0);
|
|
if (rsize < (sizeof(struct iovec) * riovcnt)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_SIZE_MISMATCHES_FIELD_VAL,
|
|
sys_call_ID_to_name[regs->sys_num], "rvec", lsize,
|
|
"liovcnt", sizeof(struct iovec) * riovcnt);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(4/*arg_num*/, regs);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (liovcnt || riovcnt) {
|
|
char __user *new_arg;
|
|
|
|
new_arg = get_user_space(lsize + rsize);
|
|
lv = (struct iovec __user *) new_arg;
|
|
rv = (struct iovec __user *)(new_arg + lsize);
|
|
|
|
if (liovcnt) {
|
|
rval = convert_array(lvec, lv, lsize,
|
|
2, liovcnt/*nr_segs*/, 0x7, 0xf);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_STRUCT_IN_ARG_NAME,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"iovec", "lvec");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (riovcnt) {
|
|
rval = convert_array(rvec, rv, rsize,
|
|
2, riovcnt/*nr_segs*/, 0x7, 0xf);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_STRUCT_IN_ARG_NAME,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"iovec", "rvec");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vm_write)
|
|
rval = sys_process_vm_writev(id, lv, liovcnt,
|
|
rv, riovcnt, flags);
|
|
else
|
|
rval = sys_process_vm_readv(id, lv, liovcnt,
|
|
rv, riovcnt, flags);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_process_vm_readv(const unsigned long pid, /* a1 */
|
|
const struct prot_iovec __user *lvec, /* a2 */
|
|
unsigned long liovcnt, /* a3 */
|
|
const struct prot_iovec __user *rvec, /* a4 */
|
|
unsigned long riovcnt, /* a5 */
|
|
unsigned long flags, /* a6 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
return protected_sys_process_vm_readwritev(pid,
|
|
lvec, liovcnt,
|
|
rvec, riovcnt,
|
|
flags, regs, 0);
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_process_vm_writev(const unsigned long pid, /* a1 */
|
|
const struct prot_iovec __user *lvec, /* a2 */
|
|
unsigned long liovcnt, /* a3 */
|
|
const struct prot_iovec __user *rvec, /* a4 */
|
|
unsigned long riovcnt, /* a5 */
|
|
unsigned long flags, /* a6 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
return protected_sys_process_vm_readwritev(pid,
|
|
lvec, liovcnt,
|
|
rvec, riovcnt,
|
|
flags, regs, 1);
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_vmsplice(int fd, /* a1 */
|
|
const struct prot_iovec __user *iov, /* a2 */
|
|
unsigned long nr_segs, /* a3 */
|
|
unsigned int flags, /* a4 */
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval = -EINVAL;
|
|
int size;
|
|
struct iovec __user *kiov;
|
|
|
|
DbgSCP("(fd=%d, iov=0x%lx, nr_segs=%ld, flg=0x%x)\n", fd, iov, nr_segs, flags);
|
|
|
|
if (fd < 0)
|
|
return -EBADF;
|
|
if (!iov || nr_segs < 0)
|
|
return -EINVAL;
|
|
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EINVAL;
|
|
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (size < sizeof(struct iovec)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"iov", size, sizeof(struct iovec));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
return rval;
|
|
}
|
|
|
|
kiov = get_user_space(size);
|
|
rval = convert_array(iov, kiov, size, 2, 1/*nr_segs*/, 0x7, 0x7);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], "iovec", 2);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_vmsplice(fd, kiov, nr_segs, flags);
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_keyctl(const int operation,
|
|
const unsigned long arg2,
|
|
const unsigned long arg3,
|
|
const unsigned long arg4,
|
|
const unsigned long arg5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval = -EINVAL;
|
|
int size;
|
|
struct iovec __user *iov;
|
|
struct iovec __user *kiov;
|
|
struct keyctl_kdf_params __user *ukdf_params;
|
|
struct keyctl_kdf_params __user *kkdf_params;
|
|
char *str_name;
|
|
|
|
switch (operation) {
|
|
case KEYCTL_INSTANTIATE_IOV:
|
|
iov = (struct iovec __user *) arg3;
|
|
if (!iov)
|
|
break;
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < sizeof(struct iovec)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"iov", size, sizeof(struct iovec));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return rval;
|
|
}
|
|
kiov = get_user_space(size);
|
|
rval = convert_array(iov, kiov, size, 2, 1/*nr_segs*/, 0x7, 0x7);
|
|
if (rval) {
|
|
str_name = "iov";
|
|
goto err_out;
|
|
}
|
|
return sys_keyctl(operation, arg2, (unsigned long) kiov,
|
|
arg4, arg5);
|
|
case KEYCTL_DH_COMPUTE:
|
|
ukdf_params = (struct keyctl_kdf_params __user *) arg5;
|
|
if (!ukdf_params)
|
|
break;
|
|
size = e2k_ptr_size(regs->args[9], regs->args[10], 0);
|
|
if (size < sizeof(struct keyctl_kdf_params)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num], "keyctl_kdf_params",
|
|
size, sizeof(struct keyctl_kdf_params));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(5/*arg_num*/, regs);
|
|
return rval;
|
|
}
|
|
kkdf_params = get_user_space(size);
|
|
rval = convert_array(ukdf_params, kkdf_params,
|
|
size, 3, 1/*nr_segs*/, 0x1f, 0x1f);
|
|
if (rval) {
|
|
str_name = "keyctl_kdf_params";
|
|
goto err_out;
|
|
}
|
|
return sys_keyctl(operation, arg2, arg3, arg4,
|
|
(unsigned long) kkdf_params);
|
|
}
|
|
|
|
return sys_keyctl(operation, arg2, arg3, arg4, arg5);
|
|
|
|
err_out:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], str_name, 2);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_prctl(const int option,
|
|
const unsigned long arg2,
|
|
const unsigned long arg3,
|
|
const unsigned long arg4,
|
|
const unsigned long arg5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval = -EINVAL;
|
|
int size, min_size;
|
|
void __user *intptr;
|
|
void __user *kintptr;
|
|
struct sock_fprog __user *sfprog;
|
|
struct sock_fprog __user *ksfprog;
|
|
char *str_name;
|
|
|
|
switch (option) {
|
|
case PR_GET_CHILD_SUBREAPER:
|
|
case PR_GET_ENDIAN:
|
|
case PR_GET_FPEMU:
|
|
case PR_GET_FPEXC:
|
|
case PR_GET_PDEATHSIG:
|
|
case PR_GET_TSC:
|
|
case PR_GET_UNALIGN:
|
|
if (!arg2)
|
|
break;
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (size < sizeof(int)) {
|
|
str_name = "(int *) arg2";
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
str_name, size, sizeof(int));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
break;
|
|
case PR_GET_NAME:
|
|
if (!arg2)
|
|
break;
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
min_size = 16; /* this is specified in Linux Pages */
|
|
if (size < min_size) {
|
|
str_name = "(char *) arg2";
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
str_name, size, min_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
break;
|
|
case PR_SET_NAME:
|
|
if (!arg2)
|
|
break;
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (e2k_ptr_str_check((char __user *) arg2, size)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NOT_STRING_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], 2/*arg#*/);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
break;
|
|
case PR_GET_TID_ADDRESS:
|
|
intptr = (void __user *) arg2;
|
|
if (!intptr)
|
|
break;
|
|
if (warn_if_not_descr(2, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
str_name = "(int **) arg2";
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (size < sizeof(int **)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
str_name, size, sizeof(int **));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
kintptr = get_user_space(size);
|
|
rval = convert_array(intptr, kintptr,
|
|
size, 1, 1/*nr_segs*/, 0x3, 0x3);
|
|
if (rval)
|
|
goto err_out;
|
|
return sys_prctl(option, (unsigned long) kintptr, arg3,
|
|
arg4, arg5);
|
|
case PR_SET_SECCOMP:
|
|
sfprog = (struct sock_fprog __user *) arg3;
|
|
if (!sfprog)
|
|
break;
|
|
if (warn_if_not_descr(3, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
str_name = "(sock_fprog *) arg3";
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < sizeof(struct sock_fprog)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
str_name, size, sizeof(struct sock_fprog));
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
ksfprog = get_user_space(size);
|
|
rval = convert_array(sfprog, ksfprog, size, 2, 1/*nr_segs*/, 0xc, 0xf);
|
|
if (rval)
|
|
goto err_out;
|
|
return sys_prctl(option, arg2, (unsigned long) ksfprog,
|
|
arg4, arg5);
|
|
}
|
|
|
|
return sys_prctl(option, arg2, arg3, arg4, arg5);
|
|
|
|
err_out:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num], str_name, 2);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_bpf(const int cmd, /* a1 */
|
|
void __user *attr, /* a2 */
|
|
const unsigned int attr_size, /* a3 */
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
void __user *attr_64 = attr;
|
|
int size_128, size_64 = sizeof(union bpf_attr);
|
|
unsigned int size = attr_size;
|
|
long rval = 0;
|
|
|
|
DbgSCP("(cmd=0x%x, attr=0x%lx, size=%d) tags=0x%lx\n",
|
|
cmd, (long) attr, size, regs->tags);
|
|
|
|
if (attr) {
|
|
int tag = (regs->tags >> 16 /*2x8*/) & 0xff;
|
|
|
|
if ((tag == ETAGAPQ) || (tag == ETAGPLD) || (tag == ETAGPLQ)) {
|
|
size_128 = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (size_128 < size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_VAL_EXCEEDS_DSCR_SIZE,
|
|
"bpf", "size", (long) size, "attr", (long) size_128);
|
|
DbgSCP("\t\tsize_128=%d, size_64=%d, size=%d\n",
|
|
size_128, size_64, size);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(2/*arg_num*/, regs);
|
|
rval = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (size < size_64)
|
|
size = size_64;
|
|
}
|
|
attr_64 = get_user_space(size);
|
|
/* NB> BPF requires unused attr fields must be zeroed! */
|
|
/* memset(attr_64, 0, size); <-- this is done in get_user_space() */
|
|
switch (cmd) {
|
|
case BPF_MAP_CREATE:
|
|
case BPF_PROG_ATTACH:
|
|
case BPF_PROG_DETACH:
|
|
case BPF_PROG_GET_NEXT_ID:
|
|
case BPF_MAP_GET_NEXT_ID:
|
|
case BPF_PROG_GET_FD_BY_ID:
|
|
case BPF_MAP_GET_FD_BY_ID:
|
|
case BPF_RAW_TRACEPOINT_OPEN:
|
|
/* No pointer in 'attr' for these commands */
|
|
attr_64 = attr;
|
|
break;
|
|
|
|
case BPF_MAP_LOOKUP_ELEM:
|
|
case BPF_MAP_UPDATE_ELEM:
|
|
case BPF_MAP_DELETE_ELEM:
|
|
case BPF_MAP_LOOKUP_AND_DELETE_ELEM:
|
|
case BPF_MAP_GET_NEXT_KEY:
|
|
#define BPF_MAP_x_ELEM_FIELDS 4
|
|
#define BPF_MAP_x_ELEM_MTYPE 0x1330
|
|
#define BPF_MAP_x_ELEM_MALIGN 0x1333
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_MAP_x_ELEM_FIELDS, 1/*items*/,
|
|
BPF_MAP_x_ELEM_MTYPE,
|
|
BPF_MAP_x_ELEM_MALIGN);
|
|
break;
|
|
|
|
case BPF_PROG_LOAD:
|
|
#define BPF_PROG_LOAD_FIELDS 14
|
|
#define BPF_PROG_LOAD_MTYPE 0x03131111131331
|
|
#define BPF_PROG_LOAD_MALIGN 0x03333111133333
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_PROG_LOAD_FIELDS, 1/*items*/,
|
|
BPF_PROG_LOAD_MTYPE,
|
|
BPF_PROG_LOAD_MALIGN);
|
|
break;
|
|
|
|
case BPF_OBJ_PIN:
|
|
case BPF_OBJ_GET:
|
|
#define BPF_OBJ_x_FIELDS 2
|
|
#define BPF_OBJ_x_MTYPE 0x13
|
|
#define BPF_OBJ_x_MALIGN 0x13
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_OBJ_x_FIELDS, 1/*items*/,
|
|
BPF_OBJ_x_MTYPE,
|
|
BPF_OBJ_x_MALIGN);
|
|
break;
|
|
|
|
case BPF_PROG_TEST_RUN:
|
|
#define BPF_PTEST_RUN_FIELDS 8
|
|
#define BPF_PTEST_RUN_MTYPE 0x33113311
|
|
#define BPF_PTEST_RUN_MALIGN 0x33113311
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_PTEST_RUN_FIELDS, 1/*items*/,
|
|
BPF_PTEST_RUN_MTYPE,
|
|
BPF_PTEST_RUN_MALIGN);
|
|
break;
|
|
|
|
case BPF_OBJ_GET_INFO_BY_FD:
|
|
#define BPF_OBJ_GET_INFO_FIELDS 2
|
|
#define BPF_OBJ_GET_INFO_MTYPE 0x31
|
|
#define BPF_OBJ_GET_INFO_MALIGN 0x33
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_OBJ_GET_INFO_FIELDS, 1/*items*/,
|
|
BPF_OBJ_GET_INFO_MTYPE,
|
|
BPF_OBJ_GET_INFO_MALIGN);
|
|
break;
|
|
|
|
case BPF_PROG_QUERY:
|
|
#define BPF_PROG_QUERY_FIELDS 4
|
|
#define BPF_PROG_QUERY_MTYPE 0x0311
|
|
#define BPF_PROG_QUERY_MALIGN 0x1311
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_PROG_QUERY_FIELDS, 1/*items*/,
|
|
BPF_PROG_QUERY_MTYPE,
|
|
BPF_PROG_QUERY_MALIGN);
|
|
break;
|
|
|
|
case BPF_BTF_LOAD:
|
|
#define BPF_BTF_LOAD_FIELDS 4
|
|
#define BPF_BTF_LOAD_MTYPE 0x0133
|
|
#define BPF_BTF_LOAD_MALIGN 0x1133
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_BTF_LOAD_FIELDS, 1/*items*/,
|
|
BPF_BTF_LOAD_MTYPE,
|
|
BPF_BTF_LOAD_MALIGN);
|
|
break;
|
|
|
|
case BPF_TASK_FD_QUERY:
|
|
#define BPF_TASK_FD_QUERY_FIELDS 6
|
|
#define BPF_TASK_FD_QUERY_MTYPE 0x111311
|
|
#define BPF_TASK_FD_QUERY_MALIGN 0x1113311
|
|
rval = get_pm_struct_simple(attr, attr_64, size,
|
|
BPF_TASK_FD_QUERY_FIELDS, 1/*items*/,
|
|
BPF_TASK_FD_QUERY_MTYPE,
|
|
BPF_TASK_FD_QUERY_MALIGN);
|
|
break;
|
|
|
|
case BPF_BTF_GET_FD_BY_ID:
|
|
case BPF_MAP_FREEZE:
|
|
case BPF_BTF_GET_NEXT_ID:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_VAL_UNSUPPORTED,
|
|
"bpf()", "CMD", cmd);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = -ENOTSUPP;
|
|
goto out;
|
|
|
|
default:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_WRONG_ARG_VALUE_LX,
|
|
"bpf()", "cmd", (long) cmd);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
rval = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_CMD_WRONG_ARG_VALUE_LX,
|
|
"bpf", "cmd", cmd, "attr", attr);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
goto out;
|
|
}
|
|
}
|
|
rval = sys_bpf(cmd, (union bpf_attr __user *) attr_64, size);
|
|
|
|
out:
|
|
DbgSCP("\treturned %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_select(int nfds, /* a1 */
|
|
fd_set __user *readfds, /* a2 */
|
|
fd_set __user *writefds, /* a3 */
|
|
fd_set __user *exceptfds, /* a4 */
|
|
struct __kernel_old_timeval __user *timeout, /* a5 */
|
|
const unsigned long unused6, /* a6 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
int max_fds, expected_size;
|
|
struct fdtable *fdt;
|
|
long rval;
|
|
|
|
if (nfds < 0)
|
|
return -EINVAL;
|
|
|
|
/* max_fds can increase, so grab it once to avoid race */
|
|
rcu_read_lock();
|
|
fdt = files_fdtable(current->files);
|
|
max_fds = fdt->max_fds;
|
|
rcu_read_unlock();
|
|
if (nfds > max_fds)
|
|
nfds = max_fds;
|
|
expected_size = (nfds + 7) / 8;
|
|
|
|
/* Check descriptor's size of 2nd argument. */
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (size && size < expected_size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"readfds", size, (size_t) expected_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check descriptor's size of 3rd argument. */
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size && size < expected_size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"writefds", size, (size_t) expected_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check descriptor's size of 4th argument. */
|
|
size = e2k_ptr_size(regs->args[7], regs->args[8], 0);
|
|
if (size && size < expected_size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"exceptfds", size, (size_t) expected_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rval = sys_select(nfds, readfds, writefds, exceptfds, timeout);
|
|
DbgSCP("sys_select(nfds=%d, ...) returned %ld\n", nfds, rval);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_pselect6(int nfds, /* a1 */
|
|
fd_set __user *readfds, /* a2 */
|
|
fd_set __user *writefds, /* a3 */
|
|
fd_set __user *exceptfds, /* a4 */
|
|
struct __kernel_timespec __user *timeout, /* a5 */
|
|
const void __user *sigmask, /* a6 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
int size;
|
|
int max_fds, expected_size;
|
|
struct fdtable *fdt;
|
|
void __user *sigmask_ptr64 = NULL;
|
|
long rval;
|
|
|
|
DbgSCP("(nfds=%d, ...)\n", nfds);
|
|
|
|
if (nfds < 0)
|
|
return -EINVAL;
|
|
|
|
/* max_fds can increase, so grab it once to avoid race */
|
|
rcu_read_lock();
|
|
fdt = files_fdtable(current->files);
|
|
max_fds = fdt->max_fds;
|
|
rcu_read_unlock();
|
|
if (nfds > max_fds)
|
|
nfds = max_fds;
|
|
expected_size = (nfds + 7) / 8;
|
|
|
|
/* Check descriptor's size of 2nd argument. */
|
|
size = e2k_ptr_size(regs->args[3], regs->args[4], 0);
|
|
if (size && size < expected_size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"readfds", size, (size_t) expected_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check descriptor's size of 3rd argument. */
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size && size < expected_size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"writefds", size, (size_t) expected_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check descriptor's size of 4th argument. */
|
|
size = e2k_ptr_size(regs->args[7], regs->args[8], 0);
|
|
if (size && size < expected_size) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"exceptfds", size, (size_t) expected_size);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (sigmask) {
|
|
#define STRUCT_SIGSET6_FIELDS 2
|
|
#define STRUCT_SIGSET6_MASK_TYPE 0x7
|
|
#define STRUCT_SIGSET6_MASK_ALIGN 0x7
|
|
#define STRUCT_SIGSET6_PROT_SIZE 24 /* sizeof(modified sigmask) in PM */
|
|
|
|
/* Check descriptor's size of 6th argument. */
|
|
size = e2k_ptr_size(regs->args[11], regs->args[12], 0);
|
|
if (size < STRUCT_SIGSET6_PROT_SIZE) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARGPTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"sigmask", size,
|
|
(size_t) STRUCT_SIGSET6_PROT_SIZE);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Translate struct sigmask from user128 to kernel64 mode. */
|
|
sigmask_ptr64 = get_user_space(size);
|
|
rval = convert_array((long __user *)sigmask, sigmask_ptr64,
|
|
size, STRUCT_SIGSET6_FIELDS, 1 /*items*/,
|
|
STRUCT_SIGSET6_MASK_TYPE,
|
|
STRUCT_SIGSET6_MASK_ALIGN);
|
|
if (rval) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"sigmask", 6);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
}
|
|
|
|
rval = sys_pselect6(nfds, readfds, writefds, exceptfds, timeout, sigmask_ptr64);
|
|
DbgSCP("sys_pselect6(nfds=%d, ...) returned %ld\n", nfds, rval);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_mincore(const unsigned long addr, /* a1 */
|
|
size_t length, /* a2 */
|
|
unsigned char __user *vec, /* a3 */
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval;
|
|
int size;
|
|
size_t min_length = (length + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
DbgSCP("addr=0x%lx, length=0x%zx, vec=0x%lx\n", addr, length, vec);
|
|
if (size > 0 && size < min_length) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_ARG_SIZE_TOO_LITTLE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
size, min_length, 3/*arg_num*/);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
length = size * PAGE_SIZE;
|
|
PROTECTED_MODE_MESSAGE(1, PMSCERRMSG_SC_ARG_COUNT_TRUNCATED, length);
|
|
}
|
|
|
|
rval = sys_mincore(addr, length, vec);
|
|
DbgSCP("sys_mincore(addr=0x%lx, length=0x%zx, vec=0x%lx) returned %ld\n",
|
|
addr, length, vec, rval);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_process_madvise(const long pidfd, /* a1 */
|
|
void __user *vec, /* a2 */
|
|
const unsigned long vlen, /* a3 */
|
|
const unsigned long behavior, /* a4 */
|
|
const unsigned long flags, /* a5 */
|
|
const unsigned long unused6, /* a6 */
|
|
const struct pt_regs *regs)
|
|
{
|
|
struct iovec __user *converted_vec = NULL;
|
|
long rval;
|
|
int err_iov;
|
|
|
|
if (vec) {
|
|
if (warn_if_not_descr(2, CHECK4DESCR_SILENT, regs))
|
|
return -EINVAL;
|
|
/* Converting iovec structure: */
|
|
/* Allocating space on user stack for converted_iovec: */
|
|
converted_vec = get_user_space(vlen * sizeof(struct iovec));
|
|
err_iov = convert_iov(vec, converted_vec, vlen, NULL/*regs*/);
|
|
if (err_iov) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"iovec", 2);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
} else {
|
|
DbgSCP("Empty struct \'iovec\' in %s\n", __func__);
|
|
}
|
|
|
|
rval = sys_process_madvise(pidfd, converted_vec, vlen, behavior, flags);
|
|
DbgSCP("%s(pidfd=%ld, ...) returned %ld\n", __func__, pidfd, rval);
|
|
|
|
return rval;
|
|
}
|
|
|
|
/*
|
|
* Converting protected structure siginfo_t into 64-bit format.
|
|
* Allocates converted structure on user stack and returns it in the 2nd arg.
|
|
* Returns error code or 0 if converted OK.
|
|
*/
|
|
|
|
/* Post-processor aimed to return syscall termination status
|
|
* from temporal structure used to run syscall back
|
|
* to original protected structure.
|
|
* 'update_all' - update whole structure (all fields); top only otherwise.
|
|
* Returns error code from put_user() or 0 if OK.
|
|
*/
|
|
static
|
|
int update_protected_siginfo_t(void __user *siginfo64,
|
|
void __user *siginfo128)
|
|
{
|
|
unsigned long __user *infop64 = siginfo64;
|
|
unsigned long __user *infop128 = siginfo128;
|
|
int rval = 0;
|
|
/*
|
|
Structure siginfo_t consists of 5 'int's + ptr/int + ...
|
|
|
|
-= 128 bit format: =- -= 64 bit format: =-
|
|
63 32 0 63 32 0
|
|
+===============|===============+ +===============|===============+
|
|
| si_errno | si_signo | 0 | si_errno | si_signo |
|
|
+===============|===============+ +===============|===============+
|
|
| XXXXXXXXXXXXX | si_code | 1 | XXXXXXXXXXXXX | si_code |
|
|
+===============|===============+ +===============|===============+
|
|
| _uid | _pid | 2 | _uid | _pid |
|
|
+===============|===============+ +===============|===============+
|
|
| XXXXXXXXXXXXX | si_status | 3 | sigval_t: {si_status/si_ptr} |
|
|
+===============|===============+ +===============|===============+
|
|
| sigval_t: {status/si_ptr(lo)} | 4 | ... |
|
|
+---------------|---------------+
|
|
| sival_ptr(hi) | 5
|
|
+===============|===============+
|
|
| ... | 6
|
|
*/
|
|
|
|
if (!infop64 || !infop128) {
|
|
DbgSCP("Empty input: siginfo64=0x%lx siginfo128=0x%lx\n",
|
|
siginfo64, siginfo128);
|
|
return rval;
|
|
}
|
|
|
|
if (copy_in_user(infop128, infop64, 32)) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_FATAL_WRITE_AT, __func__, (long) infop128);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EFAULT);
|
|
return -EFAULT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* rt_sigqueueinfo/rt_tgsigqueueinfo conversion masks siginfo_t structure: */
|
|
#define MASK_SIGINFO_T_RT_PID_UID 0xc9c400088 /* field type mask */
|
|
#define MASK_SIGINFO_T_RT 0xc9c488088 /* field type mask */
|
|
|
|
static inline
|
|
unsigned long get_siginfo_mask_on_layout(int signo, int code)
|
|
/* This function implements check similar to the one in has_si_pid_and_uid() */
|
|
{
|
|
unsigned long mask;
|
|
|
|
switch (siginfo_layout(signo, code)) {
|
|
case SIL_KILL:
|
|
case SIL_CHLD:
|
|
case SIL_RT:
|
|
mask = MASK_SIGINFO_T_RT_PID_UID;
|
|
break;
|
|
default:
|
|
mask = MASK_SIGINFO_T_RT;
|
|
break;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
static inline
|
|
unsigned long get_siginfo_mask_on_siginfo(const int __user *usiginfo)
|
|
{
|
|
int signo, code;
|
|
long mask;
|
|
|
|
if (get_user(signo, usiginfo) || get_user(code, usiginfo + 8)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_FROM,
|
|
__func__, (unsigned long __user) usiginfo);
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EFAULT);
|
|
return 0L;
|
|
}
|
|
|
|
mask = get_siginfo_mask_on_layout(signo, code);
|
|
DbgSCP("signo=%d, code=%d ==> mask = 0x%lx\n", signo, code, mask);
|
|
|
|
return mask;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_waitid(const long which, /* a1 */
|
|
const long pid, /* a2 */
|
|
void __user *infop, /* a3 */
|
|
const long options, /* a4 */
|
|
void __user *ru, /* a5 */
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
void __user *siginfo64 = NULL;
|
|
long rval;
|
|
|
|
DbgSCP("which=%ld, pid=%ld, infop=0x%lx, options=0x%x, ru=0x%lx\n",
|
|
which, pid, (long) infop, (int) options, ru);
|
|
|
|
if (infop) {
|
|
int size;
|
|
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < sizeof(siginfo_t)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
__func__, "'infop'",
|
|
size, sizeof(siginfo_t));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
/* NB> The syscall only updates the 'infop' structure.
|
|
* Therefore we don't need to convert input 'infop' structure.
|
|
*/
|
|
siginfo64 = get_user_space(sizeof(siginfo_t));
|
|
}
|
|
|
|
rval = sys_waitid((int) which, (pid_t) pid,
|
|
(struct siginfo __user *) siginfo64,
|
|
(int) options, (struct rusage __user *) ru);
|
|
if (!rval)
|
|
(void) update_protected_siginfo_t(siginfo64, infop);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_io_submit(const aio_context_t ctx_id, /* a1 */
|
|
const long nr, /* a2 */
|
|
const struct iocb __user **iocbpp, /* a3 */
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long ret;
|
|
int size;
|
|
struct iocb __user **iocbpp64;
|
|
|
|
if (!iocbpp || !nr)
|
|
return 0;
|
|
|
|
if (nr < 0)
|
|
return -EINVAL;
|
|
|
|
if (warn_if_not_descr(3, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < nr * sizeof(e2k_ptr_t)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"iocbpp", size, nr * sizeof(e2k_ptr_t));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EFAULT;
|
|
}
|
|
|
|
iocbpp64 = get_user_space(nr * sizeof(*iocbpp64));
|
|
ret = convert_array(iocbpp, iocbpp64, size, 1, nr, 0x3, 0x3);
|
|
if (ret)
|
|
return ret;
|
|
return sys_io_submit(ctx_id, nr, iocbpp64);
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_io_uring_register(const unsigned long fd, /* a1 */
|
|
const unsigned long opcode, /* a2 */
|
|
const unsigned long __user arg, /* a3 */
|
|
const unsigned long nr_args,/* a4 */
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
void __user *arg64 = (void __user *) arg;
|
|
long rval;
|
|
int size;
|
|
|
|
DbgSCP("fd=%ld, opcode=%ld, arg=0x%lx, nr_args=0x%lx\n",
|
|
fd, opcode, arg, nr_args);
|
|
|
|
if (!arg)
|
|
goto run_syscall;
|
|
|
|
switch (opcode) {
|
|
case IORING_REGISTER_BUFFERS:
|
|
/* arg points to a struct iovec array of nr_args entries */
|
|
if (warn_if_not_descr(3, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < (DESCRIPTOR_SIZE * nr_args)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num], "'arg'", size,
|
|
(DESCRIPTOR_SIZE * nr_args));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
arg64 = convert_prot_iovec_struct(arg, nr_args, 3, "arg", regs, 0);
|
|
if (unlikely(IS_ERR(arg64))) {
|
|
return PTR_ERR(arg64);
|
|
} else if (!arg64) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_NOT_DESCR_IN_SC_ARG_NAME,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"arg");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case IORING_REGISTER_FILES:
|
|
/* arg contains a pointer to an array of nr_args file ids
|
|
* (signed 32 bit integers) */
|
|
if (warn_if_not_descr(3, CHECK4DESCR_WARNING, regs))
|
|
return -EINVAL;
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < (sizeof(int) * nr_args)) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num], "'arg'", size,
|
|
(sizeof(int) * nr_args));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case IORING_REGISTER_EVENTFD:
|
|
case IORING_REGISTER_EVENTFD_ASYNC:
|
|
/* arg must contain a pointer to the eventfd file descriptor,
|
|
* and nr_args must be 1 */
|
|
if (warn_if_not_descr(3, CHECK4DESCR_WARNING, regs))
|
|
return -EINVAL;
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < (sizeof(int))) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num], "'arg'",
|
|
size, sizeof(int));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case IORING_REGISTER_RESTRICTIONS:
|
|
/* arg points to a struct io_uring_restriction array of nr_args entries */
|
|
if (warn_if_not_descr(3, CHECK4DESCR_WARNING, regs))
|
|
return -EINVAL;
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < (nr_args * sizeof(struct io_uring_restriction))) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num], "'arg'", size,
|
|
(nr_args * sizeof(struct io_uring_restriction)));
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_UNSUPP_VAL_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"opcode", opcode, 2);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
run_syscall:
|
|
rval = sys_io_uring_register((unsigned int) fd, (unsigned int) opcode,
|
|
arg64, (unsigned int) nr_args);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_kexec_load(const unsigned long entry, /* a1 */
|
|
const unsigned long nr_segments, /* a2 */
|
|
const unsigned long __user segments, /* a3 */
|
|
const unsigned long flags, /* a4 */
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
void __user *segments64;
|
|
long rval;
|
|
int size, size128;
|
|
#define KEXEC_SEGMENT_STRUCT_SIZE128 64 /* protected segment structure size */
|
|
#define KEXEC_SEGMENT_T 0x3131
|
|
#define KEXEC_SEGMENT_A 0x3331
|
|
DbgSCP("entry=%ld, nr_segments=%ld, segments=0x%lx, flags=0x%lx\n",
|
|
entry, nr_segments, segments, flags);
|
|
|
|
if (!segments || !nr_segments) {
|
|
DbgSCP("Empty segments/nr_segments: 0x%lx / %ld\n", segments, nr_segments);
|
|
return -EADDRNOTAVAIL;
|
|
}
|
|
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
size128 = KEXEC_SEGMENT_STRUCT_SIZE128 * nr_segments;
|
|
if (size < size128) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_PTR_SIZE_TOO_LITTLE,
|
|
sys_call_ID_to_name[regs->sys_num], "'segments'",
|
|
size, (size_t) size128);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EADDRNOTAVAIL;
|
|
}
|
|
|
|
segments64 = get_user_space(size128);
|
|
rval = get_pm_struct_simple((long __user *) segments, segments64, size,
|
|
4, nr_segments, KEXEC_SEGMENT_T, KEXEC_SEGMENT_A);
|
|
if (rval) {
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
return rval;
|
|
}
|
|
|
|
rval = sys_kexec_load(entry, nr_segments, segments64, flags);
|
|
|
|
return rval;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_ptrace(long request,
|
|
long pid,
|
|
unsigned long addr,
|
|
unsigned long data,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
long rval;
|
|
int ret;
|
|
|
|
/* Check for descriptors in 'addr'/'data': */
|
|
switch (request) {
|
|
case PTRACE_PEEKTEXT:
|
|
case PTRACE_PEEKDATA:
|
|
if (warn_if_not_descr(3, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
break;
|
|
case PTRACE_POKETEXT:
|
|
case PTRACE_POKEDATA:
|
|
case PTRACE_PEEKSIGINFO:
|
|
ret = warn_if_not_descr(3, CHECK4DESCR_WARNING, regs);
|
|
ret = ret ?: warn_if_not_descr(4, CHECK4DESCR_WARNING, regs);
|
|
if (ret)
|
|
return -EFAULT;
|
|
break;
|
|
case PTRACE_GETREGSET:
|
|
case PTRACE_GETREGS:
|
|
case PTRACE_SETREGS:
|
|
case PTRACE_SETREGSET:
|
|
case PTRACE_SETSIGINFO:
|
|
case PTRACE_GETSIGMASK:
|
|
case PTRACE_SETSIGMASK:
|
|
case PTRACE_SECCOMP_GET_FILTER:
|
|
case PTRACE_GET_THREAD_AREA:
|
|
case PTRACE_SET_THREAD_AREA:
|
|
case PTRACE_GET_SYSCALL_INFO:
|
|
if (warn_if_not_descr(4, CHECK4DESCR_WARNING, regs))
|
|
return -EFAULT;
|
|
break;
|
|
/* not available in e2k:
|
|
* case PTRACE_GETFPREGS:
|
|
* case PTRACE_SETFPREGS:
|
|
*/
|
|
}
|
|
|
|
if (request == PTRACE_GETREGSET) {
|
|
struct iovec __user *iovec64;
|
|
iovec64 = get_user_space(sizeof(struct iovec));
|
|
__kernel_size_t iov_len;
|
|
|
|
/* Convert struct iovec from msghdr->msg_iov */
|
|
ret = convert_iov((void __user *)data, (void __user *)iovec64, 1, NULL/*regs*/);
|
|
if (ret) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_BAD_STRUCT_IN_ARG_NAME,
|
|
sys_call_ID_to_name[regs->sys_num], "iovec", "data");
|
|
PM_EXCEPTION_IF_ORTH_MODE(SIGABRT, SI_KERNEL, EINVAL);
|
|
}
|
|
rval = sys_ptrace(request, pid, addr, (unsigned long)iovec64);
|
|
if (!rval) {
|
|
/* Updating field 'iov_len' in the iovec structute (arg 'data'): */
|
|
ret = get_user(iov_len, &iovec64->iov_len);
|
|
ret = ret ?: put_user(iov_len, &((struct prot_iovec *)data)->iov_len);
|
|
rval = (long)ret;
|
|
}
|
|
} else {
|
|
rval = sys_ptrace(request, pid, addr, data);
|
|
}
|
|
|
|
if (current->mm->context.pm_sc_debug_mode & PM_SC_DBG_MODE_COMPLEX_WRAPPERS) {
|
|
if (rval == -ESRCH)
|
|
DbgSCP("ESRCH error: Ptracee is not ready for ptrace operation??\n");
|
|
|
|
}
|
|
DbgSCP(" rval = %ld\n", rval);
|
|
return rval;
|
|
}
|
|
|
|
#define ISSUE_SIVAL_PTR_NOTE \
|
|
DbgSCP("'sigev_value.sival_ptr' must point to a cookie that is %d bytes long\n", \
|
|
NOTIFY_COOKIE_LEN)
|
|
|
|
int get_prot_sigevent(struct sigevent *event,
|
|
const struct prot_sigevent __user *u_event,
|
|
size_t sigval_sz,
|
|
const int arg_num, /* syscall arg# this event came from */
|
|
const struct pt_regs *regs)
|
|
{
|
|
DbgSCP("uevent=0x%lx, sigval_sz=%zd\n", (long)u_event, sigval_sz);
|
|
memset(event, 0, sizeof(*event));
|
|
if (!access_ok(u_event, sizeof(*u_event)) ||
|
|
__get_user(event->sigev_value.sival_int,
|
|
&u_event->sigev_value.sival_int) ||
|
|
__get_user(event->sigev_signo, &u_event->sigev_signo) ||
|
|
__get_user(event->sigev_notify, &u_event->sigev_notify) ||
|
|
__get_user(event->sigev_notify_thread_id,
|
|
&u_event->sigev_notify_thread_id)) {
|
|
return -EFAULT;
|
|
}
|
|
DbgSCP("sigev_notify=%d thread_id=%d\n",
|
|
event->sigev_notify, event->sigev_notify_thread_id);
|
|
if (sigval_sz > 0) {
|
|
int tags;
|
|
unsigned long lo, hi, p;
|
|
|
|
if (get_user_tagged_16(lo, hi, tags,
|
|
&u_event->sigev_value.sival_ptr)) {
|
|
return -EFAULT;
|
|
|
|
}
|
|
DbgSCP("tags=0x%x lo=0x%lx hi=0x%lx\n", tags, lo, hi);
|
|
if (tags != ETAGAPQ) {
|
|
if (event->sigev_notify == SIGEV_THREAD)
|
|
return -EINVAL;
|
|
return 0; /* nothing to convert */
|
|
}
|
|
p = e2k_ptr_ptr(lo, hi, sigval_sz);
|
|
if (p == 0) {
|
|
size_t size = e2k_ptr_size(lo, hi, 0);
|
|
/* NB> See SIGEV_THREAD implementation statement: */
|
|
ISSUE_SIVAL_PTR_NOTE;
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_INSUFFICIENT_STRUCT_SIZE,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
"sigev_value.sival_ptr", size, sigval_sz);
|
|
PM_BNDERR_EXCEPTION_ON_WARNING(arg_num, regs);
|
|
if (!size || PM_SYSCALL_WARN_ONLY == 0)
|
|
return -EINVAL;
|
|
p = e2k_ptr_ptr(lo, hi, 0);
|
|
}
|
|
PROTECTED_MODE_WARNING(PMSCWARN_ADDR_IN_SIGINFO,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
(unsigned long)u_event, p);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
event->sigev_value.sival_ptr = (void __user *)p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_mprotect(void *addr,
|
|
size_t len,
|
|
unsigned long prot,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
DbgSCP("addr=0x%lx/tag=0x%lx, len=0x%zx, prot=0x%lx\n",
|
|
(long)addr, PROT_SC_ARG_TAGS(1), len, prot);
|
|
if (PROT_SC_ARG_TAGS(1) == ETAGAPQ) {
|
|
e2k_ptr_lo_t descr_lo;
|
|
e2k_ptr_hi_t descr_hi;
|
|
int size; /* descriptor size */
|
|
|
|
descr_lo.word = regs->args[1];
|
|
descr_hi.word = regs->args[2];
|
|
|
|
size = descr_hi.size - descr_hi.curptr;
|
|
if (size < len) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_SC_ARG_VAL_EXCEEDS_DSCR_SIZE,
|
|
sys_call_ID_to_name[regs->sys_num], "len", len,
|
|
"addr", size);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EFAULT;
|
|
}
|
|
|
|
DbgSCP("descr_lo.fields.r/w=%d/%d\n", descr_lo.r, descr_lo.w);
|
|
/* Checking that 'prot' flags don't conflict descriptor access rights: */
|
|
if ((!descr_lo.r && (prot & PROT_READ)) ||
|
|
(!descr_lo.w && (prot & PROT_WRITE))) {
|
|
PROTECTED_MODE_WARNING(PMSCWARN_DSCR_PROT_MISMATCH,
|
|
sys_call_ID_to_name[regs->sys_num], "prot");
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EFAULT);
|
|
}
|
|
}
|
|
|
|
return sys_mprotect((unsigned long) addr, len, prot);
|
|
}
|
|
|
|
static inline
|
|
int this_is_non_empty_tag(int tag, long val)
|
|
{
|
|
if (!tag)
|
|
return 0;
|
|
/* Checking lower word tag: */
|
|
if (tag & 0x3) {
|
|
if ((tag & 0x3) != ETAGDWS)
|
|
return 1;
|
|
if (val & (int)ITAG_MASK)
|
|
return 1; /* this is diagnostic tag */
|
|
}
|
|
/* Checking higher word tag: */
|
|
if (tag >> 2) {
|
|
if ((tag >> 2) != ETAGDWS)
|
|
return 1;
|
|
if ((val & ITAG_MASK) >> 32)
|
|
return 1; /* this is diagnostic tag */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_add_key(const char __user *type,
|
|
const char __user *description,
|
|
const void __user *payload,
|
|
size_t plen,
|
|
key_serial_t destringid,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
char __user *array;
|
|
|
|
if (plen && payload) {
|
|
int size;
|
|
size_t offset;
|
|
int val_int, tag;
|
|
long val_long, next_val_long;
|
|
|
|
if (warn_if_not_descr(3, CHECK4DESCR_SILENT, regs))
|
|
return -EFAULT;
|
|
size = e2k_ptr_size(regs->args[5], regs->args[6], 0);
|
|
if (size < plen) {
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_COUNT_EXCEEDS_DESCR_SIZE, regs->sys_num,
|
|
sys_call_ID_to_name[regs->sys_num],
|
|
plen, size, 1);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(1/*arg_num*/, regs);
|
|
return -EINVAL;
|
|
}
|
|
/* Checking that payload contains tags:
|
|
* NB> Empty tags are ignored.
|
|
*/
|
|
array = (char __user *)payload;
|
|
size = plen;
|
|
/* NB> We can check only aligned part of the payload area */
|
|
/* First, we skip a few leading bytes of amount less that sizeof(int): */
|
|
offset = (uintptr_t)array & 0x3;
|
|
if (offset) {
|
|
offset = 4 - offset;
|
|
array += offset;
|
|
size -= offset;
|
|
}
|
|
/* At this point array is properly word-aligned */
|
|
/* Scanning leading unaligned word (int) if any: */
|
|
if ((uintptr_t)array & 0x7) {
|
|
if (get_user_tagged_4(val_int, tag, (int __user *) array))
|
|
goto out_error;
|
|
if (this_is_non_empty_tag(tag, (long) val_int))
|
|
goto out_warn;
|
|
array += 4;
|
|
size -= 4;
|
|
}
|
|
/* At this point array is properly dword-aligned */
|
|
/* Scanning leading unaligned dword (long) if any: */
|
|
if ((uintptr_t)array & 0xf) {
|
|
if (get_user_tagged_8(val_long, tag, (long __user *) array))
|
|
goto out_error;
|
|
if (this_is_non_empty_tag(tag, val_long))
|
|
goto out_warn;
|
|
array += 8;
|
|
size -= 8;
|
|
}
|
|
/* At this point array is properly qword-aligned */
|
|
/* Check for tags in qwords: */
|
|
for (; size >= 16; size -= 16) {
|
|
if (get_user_tagged_16(val_long, next_val_long, tag, (long __user *) array))
|
|
goto out_error;
|
|
if (this_is_non_empty_tag(tag, val_long))
|
|
goto out_warn;
|
|
if (this_is_non_empty_tag(tag >> 4, next_val_long))
|
|
goto out_warn;
|
|
array += 16;
|
|
}
|
|
/* Scanning unaligned tail dword if any: */
|
|
if (size >= 8) {
|
|
if (get_user_tagged_8(val_long, tag, (long __user *) array))
|
|
goto out_error;
|
|
if (this_is_non_empty_tag(tag, val_long))
|
|
goto out_warn;
|
|
array += 8;
|
|
size -= 8;
|
|
}
|
|
/* Scanning unaligned tail word if any: */
|
|
if (size >= 4) {
|
|
if (get_user_tagged_4(val_int, tag, (int __user *) array))
|
|
goto out_error;
|
|
if (this_is_non_empty_tag(tag, (long) val_int))
|
|
goto out_warn;
|
|
}
|
|
}
|
|
|
|
out_syscall:
|
|
return sys_add_key(type, description, payload, plen, destringid);
|
|
|
|
out_error:
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_FATAL_READ_FROM,
|
|
"add_key", (long) array);
|
|
PM_BNDERR_EXCEPTION_IF_ORTH_MODE(3/*arg_num*/, regs);
|
|
return -EFAULT;
|
|
out_warn:
|
|
PROTECTED_MODE_WARNING(PMSCWARN_TAGS_GET_LOST_WHEN_READ,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"payload", (unsigned long) array);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EFAULT);
|
|
goto out_syscall;
|
|
}
|
|
|
|
/* Returns: 1 if user memory is empty within given indexes (excluded); 0 otherwise */
|
|
static int user_prot_mem_interval_zeroed(void __user *ptr, unsigned int from, unsigned int upto)
|
|
{
|
|
char *buff;
|
|
int size, i;
|
|
|
|
size = upto - from - 1;
|
|
if (size < 0)
|
|
return 1; /* odd boundaries specified */
|
|
else if (!size)
|
|
return 0;
|
|
|
|
buff = kmalloc(size, GFP_KERNEL);
|
|
if (!buff)
|
|
return 0; /* out of kernel memory */
|
|
i = copy_from_user(buff, ((char __user *)ptr + from), size);
|
|
if (i) { /* fails to copy user memory */
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < size; i++)
|
|
if (buff[i]) {
|
|
kfree(buff);
|
|
return 0; /* non-empty byte found */
|
|
}
|
|
|
|
kfree(buff);
|
|
return 1; /* yes, it's zeroed */
|
|
}
|
|
|
|
static int check_sched_attr_struct(pid_t pid,
|
|
struct sched_attr __user *attr,
|
|
unsigned int size,
|
|
unsigned int flags,
|
|
unsigned int arg_num,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int attr_size;
|
|
|
|
if (!attr || pid < 0 || flags)
|
|
return -EINVAL;
|
|
|
|
attr_size = e2k_ptr_size(regs->args[arg_num * 2 - 1], regs->args[arg_num * 2], 0);
|
|
if (attr_size < SCHED_ATTR_SIZE_VER0) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_ARG_SIZE_TOO_LITTLE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
attr_size, (size_t) SCHED_ATTR_SIZE_VER0, arg_num);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL; /* check failed */
|
|
}
|
|
|
|
if (!size /* size is encoded within the sched_attr structure */
|
|
&& get_user(size, &attr->size))
|
|
return -EINVAL; /* check failed */
|
|
|
|
if (size > attr_size) {
|
|
PROTECTED_MODE_WARNING(PMSCERRMSG_SC_ARG_SIZE_TOO_LITTLE,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
attr_size, (size_t) size, arg_num);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -EINVAL; /* check failed */
|
|
}
|
|
|
|
if (attr_size == SCHED_ATTR_SIZE_VER0 || attr_size == SCHED_ATTR_SIZE_VER1)
|
|
return 0;
|
|
|
|
if (attr_size > SCHED_ATTR_SIZE_VER0 && attr_size < SCHED_ATTR_SIZE_VER1) {
|
|
if (user_prot_mem_interval_zeroed(attr, SCHED_ATTR_SIZE_VER0, attr_size))
|
|
return 0;
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"sched_attr", arg_num);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -E2BIG; /* check failed */
|
|
}
|
|
|
|
if (attr_size > SCHED_ATTR_SIZE_VER1) {
|
|
if (user_prot_mem_interval_zeroed(attr, SCHED_ATTR_SIZE_VER1, attr_size))
|
|
return 0;
|
|
PROTECTED_MODE_ALERT(PMSCERRMSG_BAD_STRUCT_IN_SC_ARG,
|
|
regs->sys_num, sys_call_ID_to_name[regs->sys_num],
|
|
"sched_attr", arg_num);
|
|
PM_EXCEPTION_ON_WARNING(SIGABRT, SI_KERNEL, EINVAL);
|
|
return -E2BIG; /* check failed */
|
|
}
|
|
|
|
return 0; /* check passed OK */
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_sched_setattr(pid_t pid,
|
|
struct sched_attr __user *attr,
|
|
unsigned int flags,
|
|
const unsigned long unused4,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int errnum;
|
|
|
|
errnum = check_sched_attr_struct(pid, attr, 0, flags, 2, regs);
|
|
if (errnum)
|
|
return errnum;
|
|
|
|
return sys_sched_setattr(pid, attr, flags);
|
|
}
|
|
|
|
notrace __section(".entry.text")
|
|
long protected_sys_sched_getattr(pid_t pid,
|
|
struct sched_attr __user *attr,
|
|
unsigned int size,
|
|
unsigned int flags,
|
|
const unsigned long unused5,
|
|
const unsigned long unused6,
|
|
const struct pt_regs *regs)
|
|
{
|
|
int errnum;
|
|
|
|
errnum = check_sched_attr_struct(pid, attr, size, flags, 2, regs);
|
|
if (errnum)
|
|
return errnum;
|
|
|
|
return sys_sched_getattr(pid, attr, size, flags);
|
|
}
|
|
|
|
#endif /* CONFIG_PROTECTED_MODE */
|