kernel/entry/syscall_user_dispatch.c

Source file repositories/reference/linux-study-clean/kernel/entry/syscall_user_dispatch.c

File Facts

System
Linux kernel
Corpus path
kernel/entry/syscall_user_dispatch.c
Extension
.c
Size
4349 bytes
Lines
175
Domain
Core OS
Bucket
Scheduler, Processes, Timers, Sync, And Syscalls
Inferred role
Core OS: syscall or user/kernel boundary
Status
core implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

if (unlikely(__get_user(state, sd->selector))) {
			force_exit_sig(SIGSEGV);
			return true;
		}

		if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
			return false;

		if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
			force_exit_sig(SIGSYS);
			return true;
		}
	}

	sd->on_dispatch = true;
	syscall_rollback(current, regs);
	trigger_sigsys(regs);

	return true;
}

static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned long mode,
					  unsigned long offset, unsigned long len,
					  char __user *selector)
{
	switch (mode) {
	case PR_SYS_DISPATCH_OFF:
		if (offset || len || selector)
			return -EINVAL;
		break;
	case PR_SYS_DISPATCH_EXCLUSIVE_ON:
		/*
		 * Validate the direct dispatcher region just for basic
		 * sanity against overflow and a 0-sized dispatcher
		 * region.  If the user is able to submit a syscall from
		 * an address, that address is obviously valid.
		 */
		if (offset && offset + len <= offset)
			return -EINVAL;
		break;
	case PR_SYS_DISPATCH_INCLUSIVE_ON:
		if (len == 0 || offset + len <= offset)
			return -EINVAL;
		/*
		 * Invert the range, the check in syscall_user_dispatch()
		 * supports wrap-around.
		 */
		offset = offset + len;
		len = -len;
		break;
	default:
		return -EINVAL;
	}

	/*
	 * access_ok() will clear memory tags for tagged addresses
	 * if current has memory tagging enabled.
	 *
	 * To enable a tracer to set a tracees selector the
	 * selector address must be untagged for access_ok(),
	 * otherwise an untagged tracer will always fail to set a
	 * tagged tracees selector.
	 */
	if (mode != PR_SYS_DISPATCH_OFF && selector &&
		!access_ok(untagged_addr(selector), sizeof(*selector)))
		return -EFAULT;

	task->syscall_dispatch.selector = selector;
	task->syscall_dispatch.offset = offset;
	task->syscall_dispatch.len = len;
	task->syscall_dispatch.on_dispatch = false;

	if (mode != PR_SYS_DISPATCH_OFF)
		set_task_syscall_work(task, SYSCALL_USER_DISPATCH);
	else
		clear_task_syscall_work(task, SYSCALL_USER_DISPATCH);

	return 0;
}

int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
			      unsigned long len, char __user *selector)
{
	return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
}

int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
				     void __user *data)
{
	struct syscall_user_dispatch *sd = &task->syscall_dispatch;

Annotation

Implementation Notes