<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Yes we need it, for the .permission path.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
A cached inode holds a pde reference that outlives remove_proc_entry and rundown</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
(proc_evict_inode does not drop it, only proc_free_inode does). proc_ve_permission,</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
via the .permission op, reads de-&gt;ve_perms_map as a lock free rcu reader. So if it</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
runs on that cached inode after proc_put_ve_perms and the nullify is not there, the</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
field still points at the old map. kmapset_put drops our reference and, if this pde</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
held the last one, frees it via kfree_rcu. Once the grace period passes the map is</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
freed but the field still points at it, so a later reader loads a dangling pointer</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
and reads freed memory. The nullify publishes NULL before the free, the standard rcu</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
remove then free, so the reader sees NULL and returns -EACCES, which is also correct</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
for a removed entry.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
We cannot drop the map at the pde's final free the way sysfs does, because proc can</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
free the pde from an rcu callback. When a cached inode holds the last reference, the</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
final pde_put runs from proc_free_inode (.free_inode), which is dispatched from</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
call_rcu (destroy_inode -&gt; call_rcu -&gt; i_callback -&gt; free_inode), so it runs as an</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
rcu callback and must not sleep. kmapset_put takes a mutex on the last reference</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
(kref_put_mutex on set-&gt;mutex), so it can sleep and cannot run there. That is why</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
proc drops the map in process context from proc_entry_rundown at removal, before the</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
pde is freed and while it is still reachable, which is what the nullify covers.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
sysfs does not need this. kernfs_put_ve_perms runs only from kernfs_put once the kn</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
refcount hits zero, before the kn is rcu freed, and the kernfs inode holds a count</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
reference on the kn, so a .permission reader keeps the count above zero and cannot</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
race the drop. The proc tree readers (proc_lookup_de, proc_readdir_de and the</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
ve.proc_permissions seq read) never reach a removed pde either, because</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
remove_proc_entry unlinks it under proc_subdir_lock before rundown. Only the</div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
.permission path, through a cached inode, can reach the pde after the put.</div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> Pavel Tikhomirov &lt;ptikhomirov@virtuozzo.com&gt;<br>
<b>Sent:</b> Tuesday, June 30, 2026 4:41 PM<br>
<b>To:</b> Mirian Shilakadze &lt;mirian.shilakadze@virtuozzo.com&gt;; Konstantin Khorenko &lt;khorenko@virtuozzo.com&gt;<br>
<b>Cc:</b> devel@openvz.org &lt;devel@openvz.org&gt;; den@openvz.org &lt;den@openvz.org&gt;<br>
<b>Subject:</b> Re: [PATCH vz10 7/7] fs/proc, ve: add per-VE ve.proc_permissions</font>
<div>&nbsp;</div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText"><br>
<br>
On 6/28/26 11:26, Mirian Shilakadze wrote:<br>
&gt; Add a per-VE allowlist of /proc paths exposed through the cgroup file<br>
&gt; ve.proc_permissions, the procfs counterpart of ve.sysfs_permissions.<br>
&gt; Each proc_dir_entry gains a kmapset map keyed by VE (proc_perms_key on<br>
&gt; ve_struct), so the single shared proc tree yields per-VE answers. The<br>
&gt; filesystem agnostic leaf logic is reused from fs/ve_perms.c, this commit<br>
&gt; adds the proc tree walk, the locking, and the VFS hooks: visibility in<br>
&gt; proc_lookup_de/proc_readdir_de and a .permission inode op.<br>
&gt; <br>
&gt; Paths are written relative to the proc root like sysfs, path mask where<br>
&gt; mask is r/w/x or - to remove. The host (ve0) is unaffected and an empty<br>
&gt; list exposes nothing extra. The lock-free readers load the map under rcu<br>
&gt; against the writer's copy-on-write swap, and the seq read is serialised<br>
&gt; against the writer by proc_perms_mutex.<br>
&gt; <br>
&gt; Signed-off-by: Mirian Shilakadze &lt;mirian.shilakadze@virtuozzo.com&gt;<br>
&gt; ---<br>
&gt;&nbsp; fs/proc/Makefile&nbsp;&nbsp; |&nbsp;&nbsp; 1 +<br>
&gt;&nbsp; fs/proc/generic.c&nbsp; |&nbsp; 48 ++++++-<br>
&gt;&nbsp; fs/proc/inode.c&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp; 2 +<br>
&gt;&nbsp; fs/proc/internal.h |&nbsp; 25 ++++<br>
&gt;&nbsp; fs/proc/root.c&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp; 1 +<br>
&gt;&nbsp; fs/proc/ve.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 345 +++++++++++++++++++++++++++++++++++++++++++++<br>
&gt;&nbsp; include/linux/ve.h |&nbsp;&nbsp; 1 +<br>
&gt;&nbsp; kernel/ve/ve.c&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp; 7 +<br>
&gt;&nbsp; 8 files changed, 423 insertions(+), 7 deletions(-)<br>
&gt;&nbsp; create mode 100644 fs/proc/ve.c<br>
&gt; <br>
&gt; diff --git a/fs/proc/Makefile b/fs/proc/Makefile<br>
&gt; index 7b4db9c56e6a..61a999c03663 100644<br>
&gt; --- a/fs/proc/Makefile<br>
&gt; +++ b/fs/proc/Makefile<br>
&gt; @@ -11,6 +11,7 @@ proc-$(CONFIG_MMU)&nbsp; := task_mmu.o<br>
&gt;&nbsp; <br>
&gt;&nbsp; proc-y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; += inode.o root.o base.o generic.o array.o \<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd.o<br>
&gt; +proc-$(CONFIG_VE)&nbsp;&nbsp;&nbsp; += ve.o<br>
&gt;&nbsp; proc-$(CONFIG_TTY)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; += proc_tty.o<br>
&gt;&nbsp; proc-y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; += cmdline.o<br>
&gt;&nbsp; proc-y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; += consoles.o<br>
&gt; diff --git a/fs/proc/generic.c b/fs/proc/generic.c<br>
&gt; index e8fd7c2d1c3a..791c38c49a86 100644<br>
&gt; --- a/fs/proc/generic.c<br>
&gt; +++ b/fs/proc/generic.c<br>
&gt; @@ -30,7 +30,7 @@<br>
&gt;&nbsp; <br>
&gt;&nbsp; #include &quot;internal.h&quot;<br>
&gt;&nbsp; <br>
&gt; -static DEFINE_RWLOCK(proc_subdir_lock);<br>
&gt; +DEFINE_RWLOCK(proc_subdir_lock);<br>
&gt;&nbsp; <br>
&gt;&nbsp; struct kmem_cache *proc_dir_entry_cache __ro_after_init;<br>
&gt;&nbsp; <br>
&gt; @@ -65,9 +65,9 @@ static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir)<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; subdir_node);<br>
&gt;&nbsp; }<br>
&gt;&nbsp; <br>
&gt; -static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,<br>
&gt; -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *name,<br>
&gt; -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int len)<br>
&gt; +struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *name,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int len)<br>
&gt;&nbsp; {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct rb_node *node = dir-&gt;subdir.rb_node;<br>
&gt;&nbsp; <br>
&gt; @@ -120,6 +120,31 @@ static bool proc_in_container(struct super_block *sb)<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return !ve_is_super(get_exec_env());<br>
&gt;&nbsp; }<br>
&gt;&nbsp; <br>
&gt; +/* Visible to the current VE: globally published (S_ISVTX) or per-VE allowed. */<br>
&gt; +static bool pde_visible_to_ve(struct proc_dir_entry *de)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return (de-&gt;mode &amp; S_ISVTX) || proc_d_visible(de);<br>
&gt; +}<br>
&gt; +<br>
&gt; +#ifdef CONFIG_VE<br>
&gt; +static int proc_iop_permission(struct mnt_idmap *idmap, struct inode *inode,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mask)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct proc_dir_entry *de = PDE(inode);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; int ret = 0;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; /*<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Runs safely during rcu-walk: proc_ve_permission() is a lockless rcu<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * kmapset lookup and generic_permission() copes with rcu-walk on its<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * own, so MAY_NOT_BLOCK needs no special handling here.<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (proc_in_container(inode-&gt;i_sb) &amp;&amp; !(de-&gt;mode &amp; S_ISVTX))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = proc_ve_permission(de, mask);<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return ret ? ret : generic_permission(idmap, inode, mask);<br>
&gt; +}<br>
&gt; +#endif<br>
&gt; +<br>
&gt;&nbsp; static int proc_notify_change(struct mnt_idmap *idmap,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct dentry *dentry, struct iattr *iattr)<br>
&gt;&nbsp; {<br>
&gt; @@ -167,6 +192,9 @@ static int proc_getattr(struct mnt_idmap *idmap,<br>
&gt;&nbsp; <br>
&gt;&nbsp; static const struct inode_operations proc_file_inode_operations = {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .setattr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_notify_change,<br>
&gt; +#ifdef CONFIG_VE<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; .permission&nbsp;&nbsp;&nbsp;&nbsp; = proc_iop_permission,<br>
&gt; +#endif<br>
&gt;&nbsp; };<br>
&gt;&nbsp; <br>
&gt;&nbsp; /*<br>
&gt; @@ -264,7 +292,7 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read_lock(&amp;proc_subdir_lock);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de = pde_subdir_find(de, dentry-&gt;d_name.name, dentry-&gt;d_name.len);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (de) {<br>
&gt; -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (in_container &amp;&amp; !(de-&gt;mode &amp; S_ISVTX)) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (in_container &amp;&amp; !pde_visible_to_ve(de)) {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read_unlock(&amp;proc_subdir_lock);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ERR_PTR(-ENOENT);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; @@ -317,7 +345,7 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read_unlock(&amp;proc_subdir_lock);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!in_container || (de-&gt;mode &amp; S_ISVTX)) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!in_container || pde_visible_to_ve(de)) {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!i)<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i--;<br>
&gt; @@ -328,7 +356,7 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct proc_dir_entry *next;<br>
&gt;&nbsp; <br>
&gt; -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (in_container &amp;&amp; !(de-&gt;mode &amp; S_ISVTX)) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (in_container &amp;&amp; !pde_visible_to_ve(de)) {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de = pde_subdir_next(de);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; @@ -389,6 +417,9 @@ static const struct inode_operations proc_dir_inode_operations = {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .lookup&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_lookup,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .getattr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_getattr,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .setattr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_notify_change,<br>
&gt; +#ifdef CONFIG_VE<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; .permission&nbsp;&nbsp;&nbsp;&nbsp; = proc_iop_permission,<br>
&gt; +#endif<br>
&gt;&nbsp; };<br>
&gt;&nbsp; <br>
&gt;&nbsp; /* returns the registered entry, or frees dp and returns NULL on failure */<br>
&gt; @@ -413,6 +444,7 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,<br>
&gt;&nbsp; out_free_inum:<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_free_inum(dp-&gt;low_ino);<br>
&gt;&nbsp; out_free_entry:<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; proc_put_ve_perms(dp);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pde_free(dp);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br>
&gt;&nbsp; }<br>
&gt; @@ -471,6 +503,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ent-&gt;nlink = nlink;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ent-&gt;subdir = RB_ROOT;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; refcount_set(&amp;ent-&gt;refcnt, 1);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; proc_get_ve_perms(ent);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock_init(&amp;ent-&gt;pde_unload_lock);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INIT_LIST_HEAD(&amp;ent-&gt;pde_openers);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_set_user(ent, (*parent)-&gt;uid, (*parent)-&gt;gid);<br>
&gt; @@ -498,6 +531,7 @@ struct proc_dir_entry *proc_symlink_mode(const char *name, umode_t mode,<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ent-&gt;proc_iops = &amp;proc_link_inode_operations;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ent = proc_register(parent, ent);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_put_ve_perms(ent);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pde_free(ent);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ent = NULL;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; diff --git a/fs/proc/inode.c b/fs/proc/inode.c<br>
&gt; index 5d1a75408aa4..ac943a9768f4 100644<br>
&gt; --- a/fs/proc/inode.c<br>
&gt; +++ b/fs/proc/inode.c<br>
&gt; @@ -272,6 +272,8 @@ void proc_entry_rundown(struct proc_dir_entry *de)<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_lock(&amp;de-&gt;pde_unload_lock);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spin_unlock(&amp;de-&gt;pde_unload_lock);<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; proc_put_ve_perms(de);<br>
&gt;&nbsp; }<br>
&gt;&nbsp; <br>
&gt;&nbsp; static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)<br>
&gt; diff --git a/fs/proc/internal.h b/fs/proc/internal.h<br>
&gt; index 77a517f91821..4a28da7d5dee 100644<br>
&gt; --- a/fs/proc/internal.h<br>
&gt; +++ b/fs/proc/internal.h<br>
&gt; @@ -17,6 +17,7 @@<br>
&gt;&nbsp; <br>
&gt;&nbsp; struct ctl_table_header;<br>
&gt;&nbsp; struct mempolicy;<br>
&gt; +struct kmapset_map;<br>
&gt;&nbsp; <br>
&gt;&nbsp; /*<br>
&gt;&nbsp;&nbsp; * This is not completely implemented yet. The idea is to<br>
&gt; @@ -64,6 +65,9 @@ struct proc_dir_entry {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; umode_t mode;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u8 flags;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u8 namelen;<br>
&gt; +#ifdef CONFIG_VE<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map __rcu *ve_perms_map; /* per-VE r/w/x mask for this node */<br>
&gt; +#endif<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char inline_name[];<br>
&gt;&nbsp; } __randomize_layout;<br>
&gt;&nbsp; <br>
&gt; @@ -102,6 +106,27 @@ static inline bool pde_has_proc_compat_ioctl(const struct proc_dir_entry *pde)<br>
&gt;&nbsp; extern struct kmem_cache *proc_dir_entry_cache;<br>
&gt;&nbsp; void pde_free(struct proc_dir_entry *pde);<br>
&gt;&nbsp; <br>
&gt; +extern rwlock_t proc_subdir_lock;<br>
&gt; +struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char *name, unsigned int len);<br>
&gt; +<br>
&gt; +#ifdef CONFIG_VE<br>
&gt; +void proc_init_ve_perms(void);<br>
&gt; +void proc_get_ve_perms(struct proc_dir_entry *de);<br>
&gt; +void proc_put_ve_perms(struct proc_dir_entry *de);<br>
&gt; +bool proc_d_visible(struct proc_dir_entry *de);<br>
&gt; +int proc_ve_permission(struct proc_dir_entry *de, int mask);<br>
&gt; +#else<br>
&gt; +static inline void proc_init_ve_perms(void) { }<br>
&gt; +static inline void proc_get_ve_perms(struct proc_dir_entry *de) { }<br>
&gt; +static inline void proc_put_ve_perms(struct proc_dir_entry *de) { }<br>
&gt; +static inline bool proc_d_visible(struct proc_dir_entry *de) { return false; }<br>
&gt; +static inline int proc_ve_permission(struct proc_dir_entry *de, int mask)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>
&gt; +}<br>
&gt; +#endif<br>
&gt; +<br>
&gt;&nbsp; union proc_op {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int (*proc_get_link)(struct dentry *, struct path *);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int (*proc_show)(struct seq_file *m,<br>
&gt; diff --git a/fs/proc/root.c b/fs/proc/root.c<br>
&gt; index 3f61de56ffff..9e0c5bf87602 100644<br>
&gt; --- a/fs/proc/root.c<br>
&gt; +++ b/fs/proc/root.c<br>
&gt; @@ -297,6 +297,7 @@ static struct file_system_type proc_fs_type = {<br>
&gt;&nbsp; void __init proc_root_init(void)<br>
&gt;&nbsp; {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_init_kmemcache();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; proc_init_ve_perms();<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_proc_pid_nlink();<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_self_init();<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_thread_self_init();<br>
&gt; diff --git a/fs/proc/ve.c b/fs/proc/ve.c<br>
&gt; new file mode 100644<br>
&gt; index 000000000000..10106c2a4e53<br>
&gt; --- /dev/null<br>
&gt; +++ b/fs/proc/ve.c<br>
&gt; @@ -0,0 +1,345 @@<br>
&gt; +// SPDX-License-Identifier: GPL-2.0<br>
&gt; +/*<br>
&gt; + *&nbsp; Per-VE /proc permissions (ve.proc_permissions), the procfs counterpart of<br>
&gt; + *&nbsp; the sysfs ve.sysfs_permissions mechanism. Each proc_dir_entry carries a<br>
&gt; + *&nbsp; kmapset map keyed by VE, so the single shared proc tree gives per-VE<br>
&gt; + *&nbsp; answers. The filesystem agnostic leaf logic lives in fs/ve_perms.c, this<br>
&gt; + *&nbsp; file owns the proc tree walk and the locking.<br>
&gt; + *<br>
&gt; + *&nbsp; Copyright (c) 2026 Virtuozzo International GmbH. All rights reserved.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#include &lt;linux/module.h&gt;<br>
&gt; +#include &lt;linux/errno.h&gt;<br>
&gt; +#include &lt;linux/slab.h&gt;<br>
&gt; +#include &lt;linux/string.h&gt;<br>
&gt; +#include &lt;linux/rbtree.h&gt;<br>
&gt; +#include &lt;linux/rcupdate.h&gt;<br>
&gt; +#include &lt;linux/seq_file.h&gt;<br>
&gt; +#include &lt;linux/fs.h&gt;<br>
&gt; +#include &lt;linux/cgroup.h&gt;<br>
&gt; +#include &lt;linux/ve.h&gt;<br>
&gt; +#include &lt;linux/kmapset.h&gt;<br>
&gt; +#include &lt;linux/ve-perms.h&gt;<br>
&gt; +#include &lt;linux/proc_fs.h&gt;<br>
&gt; +<br>
&gt; +#include &quot;internal.h&quot;<br>
&gt; +<br>
&gt; +struct kmapset_set proc_ve_perms_set;<br>
&gt; +<br>
&gt; +static bool proc_ve_perms_inited;<br>
&gt; +<br>
&gt; +static DEFINE_MUTEX(proc_perms_mutex);<br>
&gt; +<br>
&gt; +void __init proc_init_ve_perms(void)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map *map;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; kmapset_init_set(&amp;proc_ve_perms_set);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; map = kmapset_new(&amp;proc_ve_perms_set);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (map)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RCU_INIT_POINTER(proc_root.ve_perms_map, kmapset_commit(map));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; proc_ve_perms_inited = true;<br>
&gt; +}<br>
&gt; +<br>
&gt; +void proc_get_ve_perms(struct proc_dir_entry *de)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map *map;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (!proc_ve_perms_inited)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; map = kmapset_new(&amp;proc_ve_perms_set);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (map)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rcu_assign_pointer(de-&gt;ve_perms_map, kmapset_commit(map));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; else<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr_warn_once(&quot;proc: no ve_perms_map for %s, hidden from containers\n&quot;,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de-&gt;name);<br>
&gt; +}<br>
&gt; +<br>
&gt; +/*<br>
&gt; + * Drop the node's permission map. kmapset_put() can sleep (it takes the<br>
&gt; + * kmapset set mutex on the last reference), so every caller must be in process<br>
&gt; + * context. Registered entries drop the map from proc_entry_rundown() when they<br>
&gt; + * are removed. Entries that never reach the tree drop it on their creation error<br>
&gt; + * path (proc_register(), proc_symlink_mode()). pde_free() therefore never<br>
&gt; + * touches the map and stays safe to run from the .free_inode RCU callback, which<br>
&gt; + * is atomic. The mutex here serialises against a concurrent proc_perms_set() on<br>
&gt; + * a registered entry. The NULL fast path skips it when there is nothing to drop.<br>
&gt; + */<br>
&gt; +void proc_put_ve_perms(struct proc_dir_entry *de)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map *map;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; /* Atomic-safe fast path: already dropped at rundown, or never set. */<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (!rcu_access_pointer(de-&gt;ve_perms_map))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; /* Serialise against a concurrent proc_perms_set() on this entry. */<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; mutex_lock(&amp;proc_perms_mutex);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; map = rcu_dereference_protected(de-&gt;ve_perms_map,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lockdep_is_held(&amp;proc_perms_mutex));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_assign_pointer(de-&gt;ve_perms_map, NULL);<br>
<br>
I think that is exactly the thing why you need the second patch.<br>
Do we really need to nulify this? Can we somehow guarantee that de<br>
is not used after this and thus stale ve_perms_map is not accessed?<br>
<br>
Or alternatively should we also nulify ve_perms_map for sysfs too?<br>
<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;proc_perms_mutex);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; kmapset_put(map);<br>
&gt; +}<br>
&gt; +<br>
&gt; +bool proc_d_visible(struct proc_dir_entry *de)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct ve_struct *ve = get_exec_env();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map *map;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; bool visible;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (ve_is_super(ve))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; /*<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * proc_perms_set() can swap this map pointer concurrently and free the<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * old map through kfree_rcu(). Hold rcu across both the load and the<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * lookup so the map cannot be freed under us.<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_read_lock();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; map = rcu_dereference(de-&gt;ve_perms_map);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; visible = map &amp;&amp; ve_perms_visible(map, &amp;ve-&gt;proc_perms_key);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_read_unlock();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return visible;<br>
&gt; +}<br>
&gt; +<br>
&gt; +int proc_ve_permission(struct proc_dir_entry *de, int mask)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct ve_struct *ve = get_exec_env();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map *map;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; int ret;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (ve_is_super(ve))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_read_lock();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; map = rcu_dereference(de-&gt;ve_perms_map);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; ret = map ? ve_perms_check(map, &amp;ve-&gt;proc_perms_key, mask) : -EACCES;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_read_unlock();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static int proc_perms_set(char *path, struct ve_struct *ve, int mask)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct proc_dir_entry *de, *nde;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; char *sep = path, *dname;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; int ret = 0;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; read_lock(&amp;proc_subdir_lock);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; de = &amp;proc_root;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; pde_get(de);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; do {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dname = sep;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sep = strchr(sep, '/');<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sep)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *sep++ = 0;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!*dname)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nde = pde_subdir_find(de, dname, strlen(dname));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!nde) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read_unlock(&amp;proc_subdir_lock);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = -ENOENT;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto out;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pde_get(nde);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pde_put(de);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de = nde;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; } while (sep);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; read_unlock(&amp;proc_subdir_lock);<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; /* empty or leading-slash path walks to nothing, reject it */<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (de == &amp;proc_root) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = -EINVAL;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto out;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (!rcu_access_pointer(de-&gt;ve_perms_map)) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = -EPERM;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto out;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; ret = ve_perms_apply(&amp;de-&gt;ve_perms_map, &amp;ve-&gt;proc_perms_key,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ve_is_super(ve), mask);<br>
&gt; +out:<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; pde_put(de);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static int proc_perms_line(struct ve_struct *ve, char *line)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; int mask, ret;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; ret = ve_perms_parse(line, &amp;mask);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (ret)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return proc_perms_set(line, ve, mask);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static struct proc_dir_entry *proc_next_recursive(struct proc_dir_entry *de)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct rb_node *node;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; node = rb_first(&amp;de-&gt;subdir);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (node)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return rb_entry(node, struct proc_dir_entry, subdir_node);<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; while (de-&gt;parent != de) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node = rb_next(&amp;de-&gt;subdir_node);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (node)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return rb_entry(node, struct proc_dir_entry,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; subdir_node);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de = de-&gt;parent;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static bool proc_perms_shown(struct ve_struct *ve, struct proc_dir_entry *de)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; bool shown;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (!rcu_access_pointer(de-&gt;ve_perms_map))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; /* ve_perms_shown calls kmapset_lookup, an rcu list walk, guard it. */<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_read_lock();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; shown = ve_perms_shown(rcu_dereference(de-&gt;ve_perms_map),<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;ve-&gt;proc_perms_key, ve_is_super(ve));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; rcu_read_unlock();<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return shown;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void *proc_perms_start(struct seq_file *m, loff_t *ppos)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; __acquires(&amp;proc_subdir_lock)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct ve_struct *ve = css_to_ve(seq_css(m));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct proc_dir_entry *de;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; loff_t pos = *ppos;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; mutex_lock(&amp;proc_perms_mutex);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; read_lock(&amp;proc_subdir_lock);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; for (de = &amp;proc_root; de; de = proc_next_recursive(de)) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (proc_perms_shown(ve, de) &amp;&amp; !pos--)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return de;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void *proc_perms_next(struct seq_file *m, void *v, loff_t *ppos)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct ve_struct *ve = css_to_ve(seq_css(m));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct proc_dir_entry *de = v;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; (*ppos)++;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; while ((de = proc_next_recursive(de))) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (proc_perms_shown(ve, de))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return de;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void proc_perms_stop(struct seq_file *m, void *v)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; __releases(&amp;proc_subdir_lock)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; read_unlock(&amp;proc_subdir_lock);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;proc_perms_mutex);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static int proc_perms_show(struct seq_file *m, void *v)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct ve_struct *ve = css_to_ve(seq_css(m));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct proc_dir_entry *de = v;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_map *map;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; char *buf;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; size_t size, len, off;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; int mask;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; map = rcu_dereference_protected(de-&gt;ve_perms_map,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lockdep_is_held(&amp;proc_perms_mutex));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (ve_is_super(ve))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mask = map-&gt;default_value;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; else<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mask = kmapset_get_value(map, &amp;ve-&gt;proc_perms_key);<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; size = seq_get_buf(m, &amp;buf);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; if (size) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; off = size;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len = strlen(de-&gt;name);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (len &gt;= off) {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seq_commit(m, -1);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (S_ISDIR(de-&gt;mode))<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf[--off] = '/';<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; off -= len;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcpy(buf + off, de-&gt;name, len);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; de = de-&gt;parent;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } while (de &amp;&amp; de != &amp;proc_root);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memmove(buf, buf + off, size - off);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seq_commit(m, size - off);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; ve_perms_emit(m, mask);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static ssize_t proc_perms_write(struct kernfs_open_file *of, char *buf,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t nbytes, loff_t off)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct ve_struct *ve = css_to_ve(of_css(of));<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; char *line, *next = buf;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; int ret = -EINVAL;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; mutex_lock(&amp;proc_perms_mutex);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; do {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; line = skip_spaces(next);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!*line)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; next = strchr(line, '\n');<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (next)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *next++ = '\0';<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (*line != '#') {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = proc_perms_line(ve, line);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ret)<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; } while (next);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; mutex_unlock(&amp;proc_perms_mutex);<br>
&gt; +<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return ret ? ret : nbytes;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static struct cftype proc_ve_cftypes[] = {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;default_proc_permissions&quot;,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = CFTYPE_ONLY_ON_ROOT,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_start&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_start,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_next&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_next,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_stop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_stop,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_show&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_show,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .write&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_write,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; },<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;proc_permissions&quot;,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = CFTYPE_NOT_ON_ROOT,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_start&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_start,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_next&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_next,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_stop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_stop,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .seq_show&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_show,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .write&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = proc_perms_write,<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; },<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; { },<br>
&gt; +};<br>
&gt; +<br>
&gt; +static int init_proc_ve_perms(void)<br>
&gt; +{<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; return cgroup_add_cftypes(&amp;ve_cgrp_subsys, proc_ve_cftypes);<br>
&gt; +}<br>
&gt; +module_init(init_proc_ve_perms);<br>
&gt; diff --git a/include/linux/ve.h b/include/linux/ve.h<br>
&gt; index b037f60225bb..cba827260d07 100644<br>
&gt; --- a/include/linux/ve.h<br>
&gt; +++ b/include/linux/ve.h<br>
&gt; @@ -69,6 +69,7 @@ struct ve_struct {<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fsync_enable;<br>
&gt;&nbsp; <br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysfs_perms_key;<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; struct kmapset_key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc_perms_key;<br>
&gt;&nbsp; <br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atomic_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; netns_avail_nr;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; netns_max_nr;<br>
&gt; diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c<br>
&gt; index e58ffb22da87..d8ef28eedabd 100644<br>
&gt; --- a/kernel/ve/ve.c<br>
&gt; +++ b/kernel/ve/ve.c<br>
&gt; @@ -44,6 +44,9 @@<br>
&gt;&nbsp; #include &quot;../sched/sched.h&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* For css_tg() */<br>
&gt;&nbsp; <br>
&gt;&nbsp; extern struct kmapset_set sysfs_ve_perms_set;<br>
&gt; +#ifdef CONFIG_PROC_FS<br>
&gt; +extern struct kmapset_set proc_ve_perms_set;<br>
&gt; +#endif<br>
&gt;&nbsp; <br>
&gt;&nbsp; static struct kmem_cache *ve_cachep;<br>
&gt;&nbsp; <br>
&gt; @@ -771,6 +774,7 @@ static struct cgroup_subsys_state *ve_create(struct cgroup_subsys_state *parent_<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; init_rwsem(&amp;ve-&gt;op_sem);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INIT_LIST_HEAD(&amp;ve-&gt;ve_list);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kmapset_init_key(&amp;ve-&gt;sysfs_perms_key);<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; kmapset_init_key(&amp;ve-&gt;proc_perms_key);<br>
&gt;&nbsp; <br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atomic_set(&amp;ve-&gt;arp_neigh_nr, 0);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atomic_set(&amp;ve-&gt;nd_neigh_nr, 0);<br>
&gt; @@ -866,6 +870,9 @@ static void ve_destroy(struct cgroup_subsys_state *css)<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; free_ve_devmnts(ve);<br>
&gt;&nbsp; <br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kmapset_unlink(&amp;ve-&gt;sysfs_perms_key, &amp;sysfs_ve_perms_set);<br>
&gt; +#ifdef CONFIG_PROC_FS<br>
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; kmapset_unlink(&amp;ve-&gt;proc_perms_key, &amp;proc_ve_perms_set);<br>
&gt; +#endif<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ve_log_destroy(ve);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ve_free_vdso(ve);<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mntput(ve-&gt;devtmpfs_mnt);<br>
<br>
-- <br>
Best regards, Pavel Tikhomirov<br>
Senior Software Developer, Virtuozzo.<br>
<br>
</div>
</span></font></div>
</body>
</html>