Skip to content

Commit c95c2d7

Browse files
authored
Add mount namespace support 添加挂载命名空间支持 (tiann#2909)
1 parent d38206b commit c95c2d7

File tree

7 files changed

+255
-3
lines changed

7 files changed

+255
-3
lines changed

kernel/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernelsu-objs += pkg_observer.o
99
kernelsu-objs += setuid_hook.o
1010
kernelsu-objs += kernel_umount.o
1111
kernelsu-objs += supercalls.o
12+
kernelsu-objs += su_mount_ns.o
1213
kernelsu-objs += feature.o
1314
kernelsu-objs += ksud.o
1415
kernelsu-objs += seccomp_cache.o

kernel/allowlist.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "allowlist.h"
1919
#include "manager.h"
2020
#include "syscall_hook_manager.h"
21+
#include "su_mount_ns.h"
2122

2223
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
2324
#define FILE_FORMAT_VERSION 3 // u32
@@ -74,7 +75,7 @@ static void init_default_profiles()
7475
default_root_profile.groups[0] = 0;
7576
memcpy(&default_root_profile.capabilities.effective, &full_cap,
7677
sizeof(default_root_profile.capabilities.effective));
77-
default_root_profile.namespaces = 0;
78+
default_root_profile.namespaces = KSU_NS_INHERITED;
7879
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
7980

8081
// This means that we will umount modules by default!

kernel/app_profile.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "app_profile.h"
1212
#include "klog.h" // IWYU pragma: keep
1313
#include "selinux/selinux.h"
14+
#include "su_mount_ns.h"
1415
#include "syscall_hook_manager.h"
1516

1617
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
@@ -128,10 +129,11 @@ void escape_with_root_profile(void)
128129
spin_unlock_irq(&current->sighand->siglock);
129130

130131
setup_selinux(profile->selinux_domain);
131-
132132
for_each_thread (p, t) {
133133
ksu_set_task_tracepoint_flag(t);
134134
}
135+
136+
setup_mount_ns(profile->namespaces);
135137
}
136138

137139
void escape_to_root_for_init(void)

kernel/su_mount_ns.c

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include <linux/dcache.h>
2+
#include <linux/errno.h>
3+
#include <linux/fdtable.h>
4+
#include <linux/file.h>
5+
#include <linux/fs.h>
6+
#include <linux/fs_struct.h>
7+
#include <linux/limits.h>
8+
#include <linux/namei.h>
9+
#include <linux/proc_ns.h>
10+
#include <linux/pid.h>
11+
#include <linux/sched/task.h>
12+
#include <linux/slab.h>
13+
#include <linux/syscalls.h>
14+
#include <linux/task_work.h>
15+
#include <linux/version.h>
16+
#include <uapi/linux/mount.h>
17+
18+
#include "arch.h"
19+
#include "klog.h" // IWYU pragma: keep
20+
#include "ksu.h"
21+
#include "su_mount_ns.h"
22+
23+
extern int path_mount(const char *dev_name, struct path *path,
24+
const char *type_page, unsigned long flags,
25+
void *data_page);
26+
27+
#if defined(__aarch64__)
28+
extern long __arm64_sys_setns(const struct pt_regs *regs);
29+
#elif defined(__x86_64__)
30+
extern long __x64_sys_setns(const struct pt_regs *regs);
31+
#endif
32+
33+
static long ksu_sys_setns(int fd, int flags)
34+
{
35+
struct pt_regs regs;
36+
memset(&regs, 0, sizeof(regs));
37+
38+
PT_REGS_PARM1(&regs) = fd;
39+
PT_REGS_PARM2(&regs) = flags;
40+
41+
#if defined(__aarch64__)
42+
return __arm64_sys_setns(&regs);
43+
#elif defined(__x86_64__)
44+
return __x64_sys_setns(&regs);
45+
#else
46+
#error "Unsupported arch"
47+
#endif
48+
}
49+
50+
// global mode , need CAP_SYS_ADMIN and CAP_SYS_CHROOT to perform setns
51+
static void ksu_mnt_ns_global(void)
52+
{
53+
// save current working directory as absolute path before setns
54+
char *pwd_path = NULL;
55+
char *pwd_buf = kmalloc(PATH_MAX, GFP_KERNEL);
56+
if (!pwd_buf) {
57+
pr_warn("no mem for pwd buffer, skip restore pwd!!\n");
58+
goto try_setns;
59+
}
60+
61+
struct path saved_pwd;
62+
get_fs_pwd(current->fs, &saved_pwd);
63+
pwd_path = d_path(&saved_pwd, pwd_buf, PATH_MAX);
64+
path_put(&saved_pwd);
65+
66+
if (IS_ERR(pwd_path)) {
67+
if (PTR_ERR(pwd_path) == -ENAMETOOLONG) {
68+
pr_warn("absolute pwd longer than: %d, skip restore pwd!!\n",
69+
PATH_MAX);
70+
} else {
71+
pr_warn("get absolute pwd failed: %ld\n", PTR_ERR(pwd_path));
72+
}
73+
pwd_path = NULL;
74+
}
75+
76+
try_setns:
77+
78+
rcu_read_lock();
79+
// &init_task is not init, but swapper/idle, which forks the init process
80+
// so we need find init process
81+
struct pid *pid_struct = find_pid_ns(1, &init_pid_ns);
82+
if (unlikely(!pid_struct)) {
83+
rcu_read_unlock();
84+
pr_warn("failed to find pid_struct for PID 1\n");
85+
goto out;
86+
}
87+
88+
struct task_struct *pid1_task = get_pid_task(pid_struct, PIDTYPE_PID);
89+
rcu_read_unlock();
90+
if (unlikely(!pid1_task)) {
91+
pr_warn("failed to get task_struct for PID 1\n");
92+
goto out;
93+
}
94+
struct path ns_path;
95+
long ret = ns_get_path(&ns_path, pid1_task, &mntns_operations);
96+
put_task_struct(pid1_task);
97+
if (ret) {
98+
pr_warn("failed get path for init mount namespace: %ld\n", ret);
99+
goto out;
100+
}
101+
struct file *ns_file = dentry_open(&ns_path, O_RDONLY, ksu_cred);
102+
103+
path_put(&ns_path);
104+
if (IS_ERR(ns_file)) {
105+
pr_warn("failed open file for init mount namespace: %ld\n",
106+
PTR_ERR(ns_file));
107+
goto out;
108+
}
109+
110+
int fd = get_unused_fd_flags(O_CLOEXEC);
111+
if (fd < 0) {
112+
pr_warn("failed to get an unused fd: %d\n", fd);
113+
fput(ns_file);
114+
goto out;
115+
}
116+
117+
fd_install(fd, ns_file);
118+
ret = ksu_sys_setns(fd, CLONE_NEWNS);
119+
120+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
121+
ksys_close(fd);
122+
#else
123+
close_fd(fd);
124+
#endif
125+
126+
if (ret) {
127+
pr_warn("call setns failed: %ld\n", ret);
128+
goto out;
129+
}
130+
// try to restore working directory using absolute path after setns
131+
if (pwd_path) {
132+
struct path new_pwd;
133+
int err = kern_path(pwd_path, 0, &new_pwd);
134+
if (!err) {
135+
set_fs_pwd(current->fs, &new_pwd);
136+
path_put(&new_pwd);
137+
} else {
138+
pr_warn("restore pwd failed: %d, path: %s\n", err, pwd_path);
139+
}
140+
}
141+
out:
142+
kfree(pwd_buf);
143+
}
144+
145+
// individual mode , need CAP_SYS_ADMIN to perform unshare and remount
146+
static void ksu_mnt_ns_individual(void)
147+
{
148+
long ret = ksys_unshare(CLONE_NEWNS);
149+
if (ret) {
150+
pr_warn("call ksys_unshare failed: %ld\n", ret);
151+
return;
152+
}
153+
154+
// make root mount private
155+
struct path root_path;
156+
get_fs_root(current->fs, &root_path);
157+
int pm_ret = path_mount(NULL, &root_path, NULL, MS_PRIVATE | MS_REC, NULL);
158+
path_put(&root_path);
159+
160+
if (pm_ret < 0) {
161+
pr_err("failed to make root private, err: %d\n", pm_ret);
162+
}
163+
}
164+
165+
static void ksu_setup_mount_ns_tw_func(struct callback_head *cb)
166+
{
167+
struct ksu_mns_tw *tw = container_of(cb, struct ksu_mns_tw, cb);
168+
const struct cred *old_cred = override_creds(ksu_cred);
169+
if (tw->ns_mode == KSU_NS_GLOBAL) {
170+
ksu_mnt_ns_global();
171+
} else {
172+
ksu_mnt_ns_individual();
173+
}
174+
revert_creds(old_cred);
175+
kfree(tw);
176+
}
177+
178+
void setup_mount_ns(int32_t ns_mode)
179+
{
180+
// inherit mode
181+
if (ns_mode == KSU_NS_INHERITED) {
182+
// do nothing
183+
return;
184+
}
185+
186+
if (ns_mode != KSU_NS_GLOBAL && ns_mode != KSU_NS_INDIVIDUAL) {
187+
pr_warn("pid: %d ,unknown mount namespace mode: %d\n", current->pid,
188+
ns_mode);
189+
return;
190+
}
191+
192+
if (!ksu_cred) {
193+
pr_err("no ksu cred! skip mnt_ns magic for pid: %d.\n", current->pid);
194+
return;
195+
}
196+
197+
struct ksu_mns_tw *tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
198+
if (!tw) {
199+
pr_err("no mem for tw! skip mnt_ns magic for pid: %d.\n", current->pid);
200+
return;
201+
}
202+
tw->cb.func = ksu_setup_mount_ns_tw_func;
203+
tw->ns_mode = ns_mode;
204+
if (task_work_add(current, &tw->cb, TWA_RESUME)) {
205+
kfree(tw);
206+
pr_err("add task work failed! skip mnt_ns magic for pid: %d.\n",
207+
current->pid);
208+
}
209+
}

kernel/su_mount_ns.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef __KSU_SU_MOUNT_NS_H
2+
#define __KSU_SU_MOUNT_NS_H
3+
4+
#define KSU_NS_INHERITED 0
5+
#define KSU_NS_GLOBAL 1
6+
#define KSU_NS_INDIVIDUAL 2
7+
8+
struct ksu_mns_tw {
9+
struct callback_head cb;
10+
int32_t ns_mode;
11+
};
12+
13+
void setup_mount_ns(int32_t ns_mode);
14+
15+
#endif

manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import top.yukonga.miuix.kmp.extra.CheckboxLocation
3939
import top.yukonga.miuix.kmp.extra.SuperArrow
4040
import top.yukonga.miuix.kmp.extra.SuperCheckbox
4141
import top.yukonga.miuix.kmp.extra.SuperDialog
42+
import top.yukonga.miuix.kmp.extra.SuperDropdown
4243
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
4344

4445
@Composable
@@ -113,6 +114,15 @@ fun RootProfileConfig(
113114
)
114115
}
115116

117+
MountNameSpacePanel(profile = profile) {
118+
onProfileChange(
119+
profile.copy(
120+
namespace = it,
121+
rootUseDefault = false
122+
)
123+
)
124+
}
125+
116126
SELinuxPanel(profile = profile, onSELinuxChange = { domain, rules ->
117127
onProfileChange(
118128
profile.copy(
@@ -217,6 +227,20 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
217227

218228
}
219229

230+
@Composable
231+
fun MountNameSpacePanel(
232+
profile: Natives.Profile, onMntNamespaceChange: (namespaceType: Int) -> Unit
233+
) {
234+
SuperDropdown(
235+
title = stringResource(id = R.string.profile_namespace), items = listOf(
236+
stringResource(id = R.string.profile_namespace_inherited),
237+
stringResource(id = R.string.profile_namespace_global),
238+
stringResource(id = R.string.profile_namespace_individual),
239+
), selectedIndex = profile.namespace, onSelectedIndexChange = { index ->
240+
onMntNamespaceChange(index)
241+
})
242+
}
243+
220244
@Composable
221245
fun CapsPanel(
222246
selected: Collection<Capabilities>,

manager/app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
<string name="profile_template">模版</string>
6464
<string name="profile_custom">自定义</string>
6565
<string name="profile_name">名称</string>
66-
<string name="profile_namespace">命名空间</string>
66+
<string name="profile_namespace">挂载命名空间</string>
6767
<string name="profile_namespace_inherited">继承</string>
6868
<string name="profile_namespace_global">全局</string>
6969
<string name="profile_namespace_individual">私有</string>

0 commit comments

Comments
 (0)