← Documents Documentation/userspace-api/mfd_noexec.rst GitHub 원문 ↗

Linux 6.18.37 · 사용자 공간 API

실행 불가능한 memfd 도입

memfd 생성 시 실행 권한을 명시하고 pid namespace 정책으로 NX 기본값과 강제 수준을 제어하는 API를 설명합니다.

Source pathDocumentation/userspace-api/mfd_noexec.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

mfd_noexec.rst:1-86

보안 경계는 단일 플래그가 아니라 생성 시 의도 표시와 namespace 계층의 가장 엄격한 정책을 함께 적용해 형성됩니다. 구형 호출자에 대한 기본값과 새 호출자의 명시적 `MFD_EXEC` 허용 여부를 배포 정책에 맞춰 단계적으로 조정해야 합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ==================================
4 Introduction of non-executable mfd
5 ==================================
6 :Author:
7 Daniel Verkamp <[email protected]>
8 Jeff Xu <[email protected]>
9
10 :Contributor:
11 Aleksa Sarai <[email protected]>
12
13 Since Linux introduced the memfd feature, memfds have always had their
14 execute bit set, and the memfd_create() syscall doesn't allow setting
15 it differently.
16
17 However, in a secure-by-default system, such as ChromeOS, (where all
18 executables should come from the rootfs, which is protected by verified
19 boot), this executable nature of memfd opens a door for NoExec bypass
20 and enables “confused deputy attack”. E.g, in VRP bug [1]: cros_vm
21 process created a memfd to share the content with an external process,
22 however the memfd is overwritten and used for executing arbitrary code
23 and root escalation. [2] lists more VRP of this kind.
24
25 On the other hand, executable memfd has its legit use: runc uses memfd’s
26 seal and executable feature to copy the contents of the binary then
27 execute them. For such a system, we need a solution to differentiate runc's
28 use of executable memfds and an attacker's [3].
29
30 To address those above:
31 - Let memfd_create() set X bit at creation time.
32 - Let memfd be sealed for modifying X bit when NX is set.
33 - Add a new pid namespace sysctl: vm.memfd_noexec to help applications in
34 migrating and enforcing non-executable MFD.
35
36 User API
37 ========
38 ``int memfd_create(const char *name, unsigned int flags)``
39
40 ``MFD_NOEXEC_SEAL``
41 When MFD_NOEXEC_SEAL bit is set in the ``flags``, memfd is created
42 with NX. F_SEAL_EXEC is set and the memfd can't be modified to
43 add X later. MFD_ALLOW_SEALING is also implied.
44 This is the most common case for the application to use memfd.
45
46 ``MFD_EXEC``
47 When MFD_EXEC bit is set in the ``flags``, memfd is created with X.
48
49 Note:
50 ``MFD_NOEXEC_SEAL`` implies ``MFD_ALLOW_SEALING``. In case that
51 an app doesn't want sealing, it can add F_SEAL_SEAL after creation.
52
53
54 Sysctl:
55 ========
56 ``pid namespaced sysctl vm.memfd_noexec``
57
58 The new pid namespaced sysctl vm.memfd_noexec has 3 values:
59
60 - 0: MEMFD_NOEXEC_SCOPE_EXEC
61 memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
62 MFD_EXEC was set.
63
64 - 1: MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL
65 memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
66 MFD_NOEXEC_SEAL was set.
67
68 - 2: MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED
69 memfd_create() without MFD_NOEXEC_SEAL will be rejected.
70
71 The sysctl allows finer control of memfd_create for old software that
72 doesn't set the executable bit; for example, a container with
73 vm.memfd_noexec=1 means the old software will create non-executable memfd
74 by default while new software can create executable memfd by setting
75 MFD_EXEC.
76
77 The value of vm.memfd_noexec is passed to child namespace at creation
78 time. In addition, the setting is hierarchical, i.e. during memfd_create,
79 we will search from current ns to root ns and use the most restrictive
80 setting.
81
82 [1] https://crbug.com/1305267
83
84 [2] https://bugs.chromium.org/p/chromium/issues/list?q=type%3Dbug-security%20memfd%20escalation&can=1
85
86 [3] https://lwn.net/Articles/781013/
87

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

배경과 보안 목적

1-35

Linux에 memfd가 도입된 뒤 memfd에는 항상 실행 비트가 설정되었고, `memfd_create()` 시스템 호출에는 이를 다르게 지정하는 방법이 없었습니다. 그러나 검증 부팅으로 보호되는 rootfs에서만 실행 파일이 와야 하는 ChromeOS 같은 기본 보안 시스템에서는 이 성질이 NoExec 우회 통로가 될 수 있습니다.

문서의 VRP 사례에서는 `cros_vm` 프로세스가 외부 프로세스와 내용을 공유하려고 만든 memfd가 덮어써진 뒤 임의 코드 실행과 root 권한 상승에 사용되었습니다. 반면 runc는 바이너리 내용을 복사하고 실행하기 위해 memfd의 sealing과 실행 기능을 정당하게 사용하므로, 실행 가능한 memfd를 무조건 없애는 대신 정상 용도와 공격 용도를 구별할 수 있어야 합니다.

non-executable memfd 설계 목표
변화효과
생성 시 X 비트 선택`memfd_create()` 호출자가 실행 가능 여부를 명시
NX 상태의 X 변경 봉인나중에 실행 권한을 추가하는 우회 차단
`vm.memfd_noexec`기존 프로그램의 단계적 이전과 namespace 단위 강제

기존 호환성과 기본 보안을 함께 다루는 세 가지 변화입니다.

.. SPDX-License-Identifier: GPL-2.0

==================================
Introduction of non-executable mfd
==================================
:Author:
    Daniel Verkamp <[email protected]>
    Jeff Xu <[email protected]>

:Contributor:
	Aleksa Sarai <[email protected]>

Since Linux introduced the memfd feature, memfds have always had their
execute bit set, and the memfd_create() syscall doesn't allow setting
it differently.

However, in a secure-by-default system, such as ChromeOS, (where all
executables should come from the rootfs, which is protected by verified
boot), this executable nature of memfd opens a door for NoExec bypass
and enables “confused deputy attack”.  E.g, in VRP bug [1]: cros_vm
process created a memfd to share the content with an external process,
however the memfd is overwritten and used for executing arbitrary code
and root escalation. [2] lists more VRP of this kind.

On the other hand, executable memfd has its legit use: runc uses memfd’s
seal and executable feature to copy the contents of the binary then
execute them. For such a system, we need a solution to differentiate runc's
use of executable memfds and an attacker's [3].

To address those above:
 - Let memfd_create() set X bit at creation time.
 - Let memfd be sealed for modifying X bit when NX is set.
 - Add a new pid namespace sysctl: vm.memfd_noexec to help applications in
   migrating and enforcing non-executable MFD.

memfd_create 사용자 API

36-52

`MFD_NOEXEC_SEAL`을 `flags`에 지정하면 NX인 memfd를 만들고 `F_SEAL_EXEC`를 설정하여 나중에 X 비트를 추가하지 못하게 합니다. 이 플래그는 `MFD_ALLOW_SEALING`도 암시하며, 일반적인 애플리케이션에 권장되는 경로입니다. `MFD_EXEC`는 반대로 X가 설정된 memfd를 만듭니다.

memfd 실행 플래그
플래그생성 결과추가 규칙
`MFD_NOEXEC_SEAL`NX`F_SEAL_EXEC` 설정, X 추가 금지, `MFD_ALLOW_SEALING` 암시
`MFD_EXEC`X실행 가능한 memfd를 명시적으로 생성

생성 시점에 실행 속성과 변경 가능성을 결정합니다.

원문 주의 사항은 sealing 자체를 원하지 않는 애플리케이션이 생성 뒤 `F_SEAL_SEAL`을 추가할 수 있다고 설명합니다. `F_SEAL_SEAL`은 이후 seal 집합 변경을 막는 최종 봉인이므로, 이 문장은 로컬 v6.18.37 원문의 표현을 그대로 기준으로 이해해야 합니다.

User API
========
``int memfd_create(const char *name, unsigned int flags)``

``MFD_NOEXEC_SEAL``
	When MFD_NOEXEC_SEAL bit is set in the ``flags``, memfd is created
	with NX. F_SEAL_EXEC is set and the memfd can't be modified to
	add X later. MFD_ALLOW_SEALING is also implied.
	This is the most common case for the application to use memfd.

``MFD_EXEC``
	When MFD_EXEC bit is set in the ``flags``, memfd is created with X.

Note:
	``MFD_NOEXEC_SEAL`` implies ``MFD_ALLOW_SEALING``. In case that
	an app doesn't want sealing, it can add F_SEAL_SEAL after creation.

pid namespace sysctl

53-80

pid namespace별 `vm.memfd_noexec`는 실행 플래그를 지정하지 않는 기존 소프트웨어의 기본 동작을 조정합니다. 값이 커질수록 더 엄격하며, 새 프로그램은 정책이 허용하는 범위에서 플래그를 명시해 의도를 드러낼 수 있습니다.

vm.memfd_noexec 값
상수동작
0`MEMFD_NOEXEC_SCOPE_EXEC``MFD_EXEC`가 지정된 것처럼 실행 가능 memfd 생성
1`MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL``MFD_NOEXEC_SEAL`이 지정된 것처럼 NX 및 봉인
2`MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED``MFD_NOEXEC_SEAL`이 없는 호출 거부

`MFD_EXEC`와 `MFD_NOEXEC_SEAL`을 생략한 호출에 적용되는 정책입니다.

예를 들어 컨테이너에서 값을 1로 두면 플래그를 모르는 구형 프로그램은 기본적으로 실행 불가능한 memfd를 만들고, 새 프로그램은 `MFD_EXEC`를 명시해 실행 가능한 memfd를 요청할 수 있습니다.

계층적 정책 선택
자식 pid namespace 생성 시 부모의 vm.memfd_noexec 값 전달memfd_create 호출 시 현재 namespace부터 root namespace까지 검색검색된 설정 중 가장 제한적인 정책 선택명시한 flags와 선택된 정책에 따라 생성 또는 거부

namespace 생성 시 상속한 뒤 호출 시 가장 제한적인 값을 택합니다.


Sysctl:
========
``pid namespaced sysctl vm.memfd_noexec``

The new pid namespaced sysctl vm.memfd_noexec has 3 values:

 - 0: MEMFD_NOEXEC_SCOPE_EXEC
	memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
	MFD_EXEC was set.

 - 1: MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL
	memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
	MFD_NOEXEC_SEAL was set.

 - 2: MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED
	memfd_create() without MFD_NOEXEC_SEAL will be rejected.

The sysctl allows finer control of memfd_create for old software that
doesn't set the executable bit; for example, a container with
vm.memfd_noexec=1 means the old software will create non-executable memfd
by default while new software can create executable memfd by setting
MFD_EXEC.

The value of vm.memfd_noexec is passed to child namespace at creation
time. In addition, the setting is hierarchical, i.e. during memfd_create,
we will search from current ns to root ns and use the most restrictive
setting.

관련 사례와 참고 자료

81-86

참고 링크는 Chrome VRP 사례, memfd를 이용한 권한 상승 이슈 검색 결과, runc의 실행 가능한 memfd 사용 배경을 설명하는 LWN 기사로 이어집니다.


[1] https://crbug.com/1305267

[2] https://bugs.chromium.org/p/chromium/issues/list?q=type%3Dbug-security%20memfd%20escalation&can=1

[3] https://lwn.net/Articles/781013/