요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=====================
Syscall User Dispatch
=====================
Background
----------
Compatibility layers like Wine need a way to efficiently emulate system
calls of only a part of their process - the part that has the
incompatible code - while being able to execute native syscalls without
a high performance penalty on the native part of the process. Seccomp
falls short on this task, since it has limited support to efficiently
filter syscalls based on memory regions, and it doesn't support removing
filters. Therefore a new mechanism is necessary.
Syscall User Dispatch brings the filtering of the syscall dispatcher
address back to userspace. The application is in control of a flip
switch, indicating the current personality of the process. A
multiple-personality application can then flip the switch without
invoking the kernel, when crossing the compatibility layer API
boundaries, to enable/disable the syscall redirection and execute
syscalls directly (disabled) or send them to be emulated in userspace
through a SIGSYS.
The goal of this design is to provide very quick compatibility layer
boundary crosses, which is achieved by not executing a syscall to change
personality every time the compatibility layer executes. Instead, a
userspace memory region exposed to the kernel indicates the current
personality, and the application simply modifies that variable to
configure the mechanism.
There is a relatively high cost associated with handling signals on most
architectures, like x86, but at least for Wine, syscalls issued by
native Windows code are currently not known to be a performance problem,
since they are quite rare, at least for modern gaming applications.
Since this mechanism is designed to capture syscalls issued by
non-native applications, it must function on syscalls whose invocation
ABI is completely unexpected to Linux. Syscall User Dispatch, therefore
doesn't rely on any of the syscall ABI to make the filtering. It uses
only the syscall dispatcher address and the userspace key.
As the ABI of these intercepted syscalls is unknown to Linux, these
syscalls are not instrumentable via ptrace or the syscall tracepoints.
Interface
---------
A thread can setup this mechanism on supported kernels by executing the
following prctl:
prctl(PR_SET_SYSCALL_USER_DISPATCH, <op>, <offset>, <length>, [selector])
<op> is either PR_SYS_DISPATCH_EXCLUSIVE_ON/PR_SYS_DISPATCH_INCLUSIVE_ON
or PR_SYS_DISPATCH_OFF, to enable and disable the mechanism globally for
that thread. When PR_SYS_DISPATCH_OFF is used, the other fields must be zero.
For PR_SYS_DISPATCH_EXCLUSIVE_ON [<offset>, <offset>+<length>) delimit
a memory region interval from which syscalls are always executed directly,
regardless of the userspace selector. This provides a fast path for the
C library, which includes the most common syscall dispatchers in the native
code applications, and also provides a way for the signal handler to return
without triggering a nested SIGSYS on (rt\_)sigreturn. Users of this
interface should make sure that at least the signal trampoline code is
included in this region. In addition, for syscalls that implement the
trampoline code on the vDSO, that trampoline is never intercepted.
For PR_SYS_DISPATCH_INCLUSIVE_ON [<offset>, <offset>+<length>) delimit
a memory region interval from which syscalls are dispatched based on
the userspace selector. Syscalls from outside of the range are always
executed directly.
[selector] is a pointer to a char-sized region in the process memory
region, that provides a quick way to enable disable syscall redirection
thread-wide, without the need to invoke the kernel directly. selector
can be set to SYSCALL_DISPATCH_FILTER_ALLOW or SYSCALL_DISPATCH_FILTER_BLOCK.
Any other value should terminate the program with a SIGSYS.
Additionally, a tasks syscall user dispatch configuration can be peeked
and poked via the PTRACE_(GET|SET)_SYSCALL_USER_DISPATCH_CONFIG ptrace
requests. This is useful for checkpoint/restart software.
Security Notes
--------------
Syscall User Dispatch provides functionality for compatibility layers to
quickly capture system calls issued by a non-native part of the
application, while not impacting the Linux native regions of the
process. It is not a mechanism for sandboxing system calls, and it
should not be seen as a security mechanism, since it is trivial for a
malicious application to subvert the mechanism by jumping to an allowed
dispatcher region prior to executing the syscall, or to discover the
address and modify the selector value. If the use case requires any
kind of security sandboxing, Seccomp should be used instead.
Any fork or exec of the existing process resets the mechanism to
PR_SYS_DISPATCH_OFF.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
문서 정보
1-6이 문서는 GPL-2.0 조건의 `Syscall User Dispatch` 관리 인터페이스를 설명합니다.
배경과 설계 목표
7-47Wine 같은 호환성 계층은 프로세스의 비호환 코드 부분에서 발생한 시스템 호출만 효율적으로 에뮬레이션하면서, 네이티브 부분은 큰 성능 손실 없이 Linux 시스템 호출을 직접 실행해야 합니다. Seccomp는 메모리 영역에 따른 효율적인 필터링이 제한되고 필터를 제거할 수 없어 이 목적에 충분하지 않습니다.
Syscall User Dispatch는 시스템 호출 디스패처 주소 필터링의 제어를 사용자 공간으로 돌려줍니다. 여러 실행 인격을 가진 응용 프로그램이 호환성 계층 API 경계를 지날 때 커널 호출 없이 사용자 공간 선택기 값을 뒤집습니다. 리디렉션을 끄면 시스템 호출을 직접 실행하고, 켜면 `SIGSYS`를 통해 사용자 공간 에뮬레이터로 보냅니다.
매 경계 통과마다 인격을 바꾸는 시스템 호출을 하지 않고, 커널에 노출한 사용자 공간 메모리 한 바이트가 현재 인격을 나타내게 하므로 경계 전환이 매우 빠릅니다. x86 같은 아키텍처에서 신호 처리는 비교적 비싸지만, 현대 게임의 네이티브 Windows 코드가 내는 시스템 호출은 드물어 Wine에서는 알려진 성능 문제가 아닙니다.
비네이티브 응용 프로그램의 호출 ABI는 Linux가 전혀 예상하지 못할 수 있으므로 필터링은 시스템 호출 ABI에 의존하지 않고 디스패처 주소와 사용자 공간 키만 사용합니다. Linux가 ABI를 모르므로 가로챈 호출은 `ptrace`나 syscall tracepoint로 계측할 수 없습니다.
prctl 인터페이스
48-84지원 커널에서 스레드는 다음 `prctl`로 Syscall User Dispatch를 설정합니다.
prctl(PR_SET_SYSCALL_USER_DISPATCH, <op>, <offset>, <length>, [selector])
`op`는 스레드 전체 기능을 켜는 `PR_SYS_DISPATCH_EXCLUSIVE_ON` 또는 `PR_SYS_DISPATCH_INCLUSIVE_ON`, 끄는 `PR_SYS_DISPATCH_OFF` 중 하나입니다. 끌 때 나머지 필드는 0이어야 합니다.
배타 모드에서 `[offset, offset+length)`는 선택기와 관계없이 항상 시스템 호출을 직접 실행하는 메모리 구간입니다. 일반적인 네이티브 디스패처가 있는 C 라이브러리의 빠른 경로를 제공하고, 신호 처리기가 `(rt_)sigreturn` 중 중첩 `SIGSYS` 없이 돌아오게 합니다. 최소한 신호 트램펄린 코드를 이 영역에 넣어야 합니다. vDSO가 트램펄린을 구현하는 호출은 그 트램펄린을 가로채지 않습니다.
포함 모드에서 `[offset, offset+length)` 안의 호출은 사용자 공간 선택기에 따라 디스패치하고, 범위 밖 호출은 항상 직접 실행합니다.
`selector`는 프로세스 메모리의 `char` 크기 영역을 가리키는 포인터입니다. 커널을 직접 호출하지 않고 스레드 전체 리디렉션을 빠르게 켜고 끕니다. 값은 `SYSCALL_DISPATCH_FILTER_ALLOW` 또는 `SYSCALL_DISPATCH_FILTER_BLOCK`이어야 하며 다른 값이면 `SIGSYS`로 프로그램을 종료해야 합니다.
체크포인트·재시작 소프트웨어는 `PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG`와 `PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG` ptrace 요청으로 작업의 설정을 읽고 쓸 수 있습니다.
모드와 호출 주소, 사용자 공간 selector가 직접 실행 또는 SIGSYS 리디렉션을 결정합니다.
보안 주의 사항
85-99이 기능은 응용 프로그램의 Linux 네이티브 영역에 영향을 주지 않으면서 비네이티브 부분의 시스템 호출을 빠르게 가로채기 위한 호환성 기능입니다. 시스템 호출 샌드박스가 아니며 보안 메커니즘으로 간주하면 안 됩니다.
악성 응용 프로그램은 호출 전에 허용된 디스패처 영역으로 점프하거나 selector 주소를 찾아 값을 바꾸어 쉽게 우회할 수 있습니다. 보안 샌드박스가 필요하면 Seccomp를 사용합니다. 기존 프로세스가 `fork`하거나 `exec`하면 설정은 `PR_SYS_DISPATCH_OFF`로 초기화됩니다.
요약과 해설
syscall-user-dispatch.rst:1-99Syscall User Dispatch는 보안 필터가 아니라 호환성 계층의 빠른 인격 전환 장치입니다. selector 한 바이트를 바꾸는 것으로 커널 호출 없이 리디렉션 상태를 전환합니다.
배타·포함 모드의 주소 구간 의미가 반대이며 신호 트램펄린과 vDSO 예외를 고려해야 합니다. 악성 프로세스가 selector를 직접 바꿀 수 있으므로 보안 격리는 Seccomp로 구현합니다.