lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Sat, 25 Jun 2016 02:23:30 +0200
From:	Jann Horn <jannh@...gle.com>
To:	Andrew Morton <akpm@...ux-foundation.org>,
	Kees Cook <keescook@...omium.org>,
	Al Viro <viro@...iv.linux.org.uk>,
	Cyrill Gorcunov <gorcunov@...nvz.org>,
	Alexey Dobriyan <adobriyan@...il.com>,
	John Stultz <john.stultz@...aro.org>,
	Janis Danisevskis <jdanis@...gle.com>,
	Calvin Owens <calvinowens@...com>, Jann Horn <jann@...jh.net>,
	Oleg Nesterov <oleg@...hat.com>,
	Christoph Lameter <cl@...ux.com>,
	"Eric W. Biederman" <ebiederm@...ssion.com>,
	Andy Lutomirski <luto@...nel.org>, linux-kernel@...r.kernel.org
Cc:	Jann Horn <jannh@...gle.com>
Subject: [PATCH v2 2/2] namespaces: add transparent user namespaces

This allows the admin of a user namespace to mark the namespace as
transparent. All other namespaces, by default, are opaque.

While the current behavior of user namespaces is appropriate for use in
containers, there are many programs that only use user namespaces because
doing so enables them to do other things (e.g. unsharing the mount or
network namespace) that require namespaced capabilities. For them, the
inability to see the real UIDs and GIDs of things from inside the user
namespace can be very annoying.

In a transparent namespace, all UIDs and GIDs that are mapped into its
first opaque ancestor are visible and are not remapped. This means that if
a process e.g. stat()s the real root directory in a namespace, it will
still see it as owned by UID 0.

Traditionally, any UID or GID that was visible in a user namespace was also
mapped into the namespace, giving the namespace admin full access to it.
This patch introduces a distinction: In a transparent namespace, UIDs and
GIDs can be visible without being mapped. Non-mapped, visible UIDs can be
passed from the kernel to userspace, but userspace can't send them back to
the kernel. In order to be able to fully use specific UIDs/GIDs and gain
privileges over them, mappings need to be set up in the usual way -
however, to avoid aliasing problems, only identity mappings are permitted.

v2:
Ensure that all relevant from_k[ug]id callers show up in the patch.
_transparent would be more verbose than _tp, but considering the line
length rule, that's just too long.

Yes, this makes the patch rather large.

Behavior should be the same as in v1, except that I'm not touching orangefs
in this patch because every single use of from_k[ug]id in it is wrong in
some way. (Thanks for making me reread all that stuff, Eric.) I'll write a
separate patch or at least report the issue with more detail later.

(Also, the handling of user namespaces when dealing with signals is
super-ugly and kind of incorrect. That should probably be cleaned up.)

posix_acl_to_xattr would have changed behavior in the v1 patch, but isn't
changed here. Because it's only used with init_user_ns, that won't change
user-visible behavior relative to v1.

This patch was compile-tested with allyesconfig. I also ran a VM with this
patch applied and checked that it still works, but that probably doesn't
mean much.

Signed-off-by: Jann Horn <jannh@...gle.com>
---
 arch/alpha/kernel/osf_sys.c       |   4 +-
 arch/arm/kernel/sys_oabi-compat.c |   4 +-
 arch/ia64/kernel/signal.c         |   4 +-
 arch/s390/kernel/compat_linux.c   |  26 +++---
 arch/sparc/kernel/sys_sparc32.c   |   4 +-
 arch/x86/ia32/sys_ia32.c          |   4 +-
 drivers/android/binder.c          |   2 +-
 drivers/gpu/drm/drm_info.c        |   2 +-
 drivers/gpu/drm/drm_ioctl.c       |   2 +-
 drivers/net/tun.c                 |   4 +-
 fs/autofs4/dev-ioctl.c            |   4 +-
 fs/autofs4/waitq.c                |   4 +-
 fs/binfmt_elf.c                   |  12 +--
 fs/binfmt_elf_fdpic.c             |  12 +--
 fs/compat.c                       |   4 +-
 fs/fcntl.c                        |   4 +-
 fs/ncpfs/ioctl.c                  |  12 +--
 fs/posix_acl.c                    |  11 ++-
 fs/proc/array.c                   |  18 ++--
 fs/proc/base.c                    |  30 +++++--
 fs/quota/kqid.c                   |  12 ++-
 fs/stat.c                         |  12 +--
 include/linux/uidgid.h            |  24 +++--
 include/linux/user_namespace.h    |   4 +
 include/net/scm.h                 |   4 +-
 ipc/mqueue.c                      |   2 +-
 ipc/msg.c                         |   8 +-
 ipc/sem.c                         |   8 +-
 ipc/shm.c                         |   8 +-
 ipc/util.c                        |   8 +-
 kernel/acct.c                     |   4 +-
 kernel/exit.c                     |   6 +-
 kernel/groups.c                   |   2 +-
 kernel/signal.c                   |  16 ++--
 kernel/sys.c                      |  24 ++---
 kernel/trace/trace.c              |   2 +-
 kernel/tsacct.c                   |   4 +-
 kernel/uid16.c                    |  22 ++---
 kernel/user.c                     |   1 +
 kernel/user_namespace.c           | 178 +++++++++++++++++++++++++++++++++++---
 net/appletalk/atalk_proc.c        |   2 +-
 net/ax25/ax25_uid.c               |   4 +-
 net/bluetooth/af_bluetooth.c      |   2 +-
 net/core/sock.c                   |   4 +-
 net/ipv4/inet_diag.c              |   2 +-
 net/ipv4/ping.c                   |   2 +-
 net/ipv4/raw.c                    |   2 +-
 net/ipv4/sysctl_net_ipv4.c        |   4 +-
 net/ipv4/tcp_ipv4.c               |   6 +-
 net/ipv4/udp.c                    |   2 +-
 net/ipv6/datagram.c               |   2 +-
 net/ipv6/ip6_flowlabel.c          |   2 +-
 net/ipv6/tcp_ipv6.c               |   6 +-
 net/ipx/ipx_proc.c                |   2 +-
 net/key/af_key.c                  |   2 +-
 net/llc/llc_proc.c                |   2 +-
 net/netfilter/nfnetlink_log.c     |   4 +-
 net/packet/af_packet.c            |   2 +-
 net/packet/diag.c                 |   2 +-
 net/phonet/socket.c               |   4 +-
 net/sctp/proc.c                   |   4 +-
 net/sunrpc/svcauth_unix.c         |   4 +-
 security/keys/keyctl.c            |   4 +-
 security/keys/proc.c              |   6 +-
 64 files changed, 395 insertions(+), 197 deletions(-)

diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index ffb93f49..6440f8e 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -277,8 +277,8 @@ linux_to_osf_stat(struct kstat *lstat, struct osf_stat __user *osf_stat)
 	tmp.st_dev	= lstat->dev;
 	tmp.st_mode	= lstat->mode;
 	tmp.st_nlink	= lstat->nlink;
-	tmp.st_uid	= from_kuid_munged(current_user_ns(), lstat->uid);
-	tmp.st_gid	= from_kgid_munged(current_user_ns(), lstat->gid);
+	tmp.st_uid	= from_kuid_tp_munged(current_user_ns(), lstat->uid);
+	tmp.st_gid	= from_kgid_tp_munged(current_user_ns(), lstat->gid);
 	tmp.st_rdev	= lstat->rdev;
 	tmp.st_ldev	= lstat->rdev;
 	tmp.st_size	= lstat->size;
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
index 087acb5..47748eb 100644
--- a/arch/arm/kernel/sys_oabi-compat.c
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -124,8 +124,8 @@ static long cp_oldabi_stat64(struct kstat *stat,
 	tmp.__st_ino = stat->ino;
 	tmp.st_mode = stat->mode;
 	tmp.st_nlink = stat->nlink;
-	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
-	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
+	tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
+	tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
 	tmp.st_rdev = huge_encode_dev(stat->rdev);
 	tmp.st_size = stat->size;
 	tmp.st_blocks = stat->blocks;
diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
index b3a124d..071b9c0 100644
--- a/arch/ia64/kernel/signal.c
+++ b/arch/ia64/kernel/signal.c
@@ -209,7 +209,7 @@ ia64_rt_sigreturn (struct sigscratch *scr)
 	si.si_errno = 0;
 	si.si_code = SI_KERNEL;
 	si.si_pid = task_pid_vnr(current);
-	si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 	si.si_addr = sc;
 	force_sig_info(SIGSEGV, &si, current);
 	return retval;
@@ -306,7 +306,7 @@ force_sigsegv_info (int sig, void __user *addr)
 	si.si_errno = 0;
 	si.si_code = SI_KERNEL;
 	si.si_pid = task_pid_vnr(current);
-	si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	si.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 	si.si_addr = addr;
 	force_sig_info(SIGSEGV, &si, current);
 	return 1;
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 437e611..31a39ba 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -136,9 +136,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresuid16, u16 __user *, ruidp,
 	int retval;
 	u16 ruid, euid, suid;
 
-	ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
-	euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
-	suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
+	ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
+	euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
+	suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
 
 	if (!(retval   = put_user(ruid, ruidp)) &&
 	    !(retval   = put_user(euid, euidp)))
@@ -160,9 +160,9 @@ COMPAT_SYSCALL_DEFINE3(s390_getresgid16, u16 __user *, rgidp,
 	int retval;
 	u16 rgid, egid, sgid;
 
-	rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
-	egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
-	sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
+	rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
+	egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
+	sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
 
 	if (!(retval   = put_user(rgid, rgidp)) &&
 	    !(retval   = put_user(egid, egidp)))
@@ -190,7 +190,7 @@ static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info
 
 	for (i = 0; i < group_info->ngroups; i++) {
 		kgid = GROUP_AT(group_info, i);
-		group = (u16)from_kgid_munged(user_ns, kgid);
+		group = (u16)from_kgid_tp_munged(user_ns, kgid);
 		if (put_user(group, grouplist+i))
 			return -EFAULT;
 	}
@@ -271,22 +271,22 @@ COMPAT_SYSCALL_DEFINE2(s390_setgroups16, int, gidsetsize, u16 __user *, grouplis
 
 COMPAT_SYSCALL_DEFINE0(s390_getuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
 }
 
 COMPAT_SYSCALL_DEFINE0(s390_geteuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
 }
 
 COMPAT_SYSCALL_DEFINE0(s390_getgid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
 }
 
 COMPAT_SYSCALL_DEFINE0(s390_getegid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
 }
 
 #ifdef CONFIG_SYSVIPC
@@ -366,8 +366,8 @@ static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat)
 	tmp.__st_ino = (u32)stat->ino;
 	tmp.st_mode = stat->mode;
 	tmp.st_nlink = (unsigned int)stat->nlink;
-	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
-	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
+	tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
+	tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
 	tmp.st_rdev = huge_encode_dev(stat->rdev);
 	tmp.st_size = stat->size;
 	tmp.st_blksize = (u32)stat->blksize;
diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index 022c30c..e65acab 100644
--- a/arch/sparc/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
@@ -76,8 +76,8 @@ static int cp_compat_stat64(struct kstat *stat,
 	err |= put_user(stat->ino, &statbuf->st_ino);
 	err |= put_user(stat->mode, &statbuf->st_mode);
 	err |= put_user(stat->nlink, &statbuf->st_nlink);
-	err |= put_user(from_kuid_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
-	err |= put_user(from_kgid_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
+	err |= put_user(from_kuid_tp_munged(current_user_ns(), stat->uid), &statbuf->st_uid);
+	err |= put_user(from_kgid_tp_munged(current_user_ns(), stat->gid), &statbuf->st_gid);
 	err |= put_user(huge_encode_dev(stat->rdev), &statbuf->st_rdev);
 	err |= put_user(0, (unsigned long __user *) &statbuf->__pad3[0]);
 	err |= put_user(stat->size, &statbuf->st_size);
diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
index 719cd70..e8d4532 100644
--- a/arch/x86/ia32/sys_ia32.c
+++ b/arch/x86/ia32/sys_ia32.c
@@ -71,8 +71,8 @@ static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat)
 {
 	typeof(ubuf->st_uid) uid = 0;
 	typeof(ubuf->st_gid) gid = 0;
-	SET_UID(uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	if (!access_ok(VERIFY_WRITE, ubuf, sizeof(struct stat64)) ||
 	    __put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) ||
 	    __put_user(stat->ino, &ubuf->__st_ino) ||
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 16288e7..c8fcf71 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -2400,7 +2400,7 @@ retry:
 		}
 		tr.code = t->code;
 		tr.flags = t->flags;
-		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
+		tr.sender_euid = from_kuid_tp(current_user_ns(), t->sender_euid);
 
 		if (t->from) {
 			struct task_struct *sender = t->from->proc->tsk;
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 5d469b2..07ab3d4 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -186,7 +186,7 @@ int drm_clients_info(struct seq_file *m, void *data)
 			   priv->minor->index,
 			   priv->is_master ? 'y' : 'n',
 			   priv->authenticated ? 'y' : 'n',
-			   from_kuid_munged(seq_user_ns(m), priv->uid),
+			   from_kuid_tp_munged(seq_user_ns(m), priv->uid),
 			   priv->magic);
 		rcu_read_unlock();
 	}
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index b7a39771c..2e18837 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -181,7 +181,7 @@ static int drm_getclient(struct drm_device *dev, void *data,
 	if (client->idx == 0) {
 		client->auth = file_priv->authenticated;
 		client->pid = pid_vnr(file_priv->pid);
-		client->uid = from_kuid_munged(current_user_ns(),
+		client->uid = from_kuid_tp_munged(current_user_ns(),
 					       file_priv->uid);
 		client->magic = 0;
 		client->iocs = 0;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index e16487c..8965a26 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1659,7 +1659,7 @@ static ssize_t tun_show_owner(struct device *dev, struct device_attribute *attr,
 	struct tun_struct *tun = netdev_priv(to_net_dev(dev));
 	return uid_valid(tun->owner)?
 		sprintf(buf, "%u\n",
-			from_kuid_munged(current_user_ns(), tun->owner)):
+			from_kuid_tp_munged(current_user_ns(), tun->owner)) :
 		sprintf(buf, "-1\n");
 }
 
@@ -1669,7 +1669,7 @@ static ssize_t tun_show_group(struct device *dev, struct device_attribute *attr,
 	struct tun_struct *tun = netdev_priv(to_net_dev(dev));
 	return gid_valid(tun->group) ?
 		sprintf(buf, "%u\n",
-			from_kgid_munged(current_user_ns(), tun->group)):
+			from_kgid_tp_munged(current_user_ns(), tun->group)) :
 		sprintf(buf, "-1\n");
 }
 
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index c7fcc74..4c75a4c 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -460,9 +460,9 @@ static int autofs_dev_ioctl_requester(struct file *fp,
 		autofs4_expire_wait(path.dentry, 0);
 		spin_lock(&sbi->fs_lock);
 		param->requester.uid =
-			from_kuid_munged(current_user_ns(), ino->uid);
+			from_kuid_tp_munged(current_user_ns(), ino->uid);
 		param->requester.gid =
-			from_kgid_munged(current_user_ns(), ino->gid);
+			from_kgid_tp_munged(current_user_ns(), ino->gid);
 		spin_unlock(&sbi->fs_lock);
 	}
 	path_put(&path);
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 0146d91..7d55752 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -157,8 +157,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
 		packet->name[wq->name.len] = '\0';
 		packet->dev = wq->dev;
 		packet->ino = wq->ino;
-		packet->uid = from_kuid_munged(user_ns, wq->uid);
-		packet->gid = from_kgid_munged(user_ns, wq->gid);
+		packet->uid = from_kuid_tp_munged(user_ns, wq->uid);
+		packet->gid = from_kgid_tp_munged(user_ns, wq->gid);
 		packet->pid = wq->pid;
 		packet->tgid = wq->tgid;
 		break;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index a7a28110..3f9be45 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -240,10 +240,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	NEW_AUX_ENT(AT_BASE, interp_load_addr);
 	NEW_AUX_ENT(AT_FLAGS, 0);
 	NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
-	NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
-	NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
-	NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
-	NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
+	NEW_AUX_ENT(AT_UID, from_kuid_tp_munged(cred->user_ns, cred->uid));
+	NEW_AUX_ENT(AT_EUID, from_kuid_tp_munged(cred->user_ns, cred->euid));
+	NEW_AUX_ENT(AT_GID, from_kgid_tp_munged(cred->user_ns, cred->gid));
+	NEW_AUX_ENT(AT_EGID, from_kgid_tp_munged(cred->user_ns, cred->egid));
  	NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
 	NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
 #ifdef ELF_HWCAP2
@@ -1474,8 +1474,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
 	psinfo->pr_flag = p->flags;
 	rcu_read_lock();
 	cred = __task_cred(p);
-	SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
-	SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
+	SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
+	SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
 	rcu_read_unlock();
 	strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
 	
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 2035893..9d76bb7 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -644,10 +644,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
 	NEW_AUX_ENT(AT_BASE,	interp_params->elfhdr_addr);
 	NEW_AUX_ENT(AT_FLAGS,	0);
 	NEW_AUX_ENT(AT_ENTRY,	exec_params->entry_addr);
-	NEW_AUX_ENT(AT_UID,	(elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid));
-	NEW_AUX_ENT(AT_EUID,	(elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid));
-	NEW_AUX_ENT(AT_GID,	(elf_addr_t) from_kgid_munged(cred->user_ns, cred->gid));
-	NEW_AUX_ENT(AT_EGID,	(elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid));
+	NEW_AUX_ENT(AT_UID,	(elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->uid));
+	NEW_AUX_ENT(AT_EUID,	(elf_addr_t) from_kuid_tp_munged(cred->user_ns, cred->euid));
+	NEW_AUX_ENT(AT_GID,	(elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->gid));
+	NEW_AUX_ENT(AT_EGID,	(elf_addr_t) from_kgid_tp_munged(cred->user_ns, cred->egid));
 	NEW_AUX_ENT(AT_SECURE,	security_bprm_secureexec(bprm));
 	NEW_AUX_ENT(AT_EXECFN,	bprm->exec);
 
@@ -1434,8 +1434,8 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
 	psinfo->pr_flag = p->flags;
 	rcu_read_lock();
 	cred = __task_cred(p);
-	SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
-	SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
+	SET_UID(psinfo->pr_uid, from_kuid_tp_munged(cred->user_ns, cred->uid));
+	SET_GID(psinfo->pr_gid, from_kgid_tp_munged(cred->user_ns, cred->gid));
 	rcu_read_unlock();
 	strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
 
diff --git a/fs/compat.c b/fs/compat.c
index be6e48b..8e3cb5d3 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -142,8 +142,8 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
 	tmp.st_nlink = stat->nlink;
 	if (tmp.st_nlink != stat->nlink)
 		return -EOVERFLOW;
-	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	tmp.st_rdev = old_encode_dev(stat->rdev);
 	if ((u64) stat->size > MAX_NON_LFS)
 		return -EOVERFLOW;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 350a2c8..bcba367 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -225,8 +225,8 @@ static int f_getowner_uids(struct file *filp, unsigned long arg)
 	int err;
 
 	read_lock(&filp->f_owner.lock);
-	src[0] = from_kuid(user_ns, filp->f_owner.uid);
-	src[1] = from_kuid(user_ns, filp->f_owner.euid);
+	src[0] = from_kuid_tp(user_ns, filp->f_owner.uid);
+	src[1] = from_kuid_tp(user_ns, filp->f_owner.euid);
 	read_unlock(&filp->f_owner.lock);
 
 	err  = put_user(src[0], &dst[0]);
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 0a3f9b5..65631c2 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -45,7 +45,7 @@ ncp_get_fs_info(struct ncp_server * server, struct inode *inode,
 		return -EINVAL;
 	}
 	/* TODO: info.addr = server->m.serv_addr; */
-	SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
+	SET_UID(info.mounted_uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
 	info.connection		= server->connection;
 	info.buffer_size	= server->buffer_size;
 	info.volume_number	= NCP_FINFO(inode)->volNumber;
@@ -69,7 +69,7 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode,
 		ncp_dbg(1, "info.version invalid: %d\n", info2.version);
 		return -EINVAL;
 	}
-	info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+	info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 	info2.connection    = server->connection;
 	info2.buffer_size   = server->buffer_size;
 	info2.volume_number = NCP_FINFO(inode)->volNumber;
@@ -135,7 +135,7 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode,
 		ncp_dbg(1, "info.version invalid: %d\n", info2.version);
 		return -EINVAL;
 	}
-	info2.mounted_uid   = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+	info2.mounted_uid   = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 	info2.connection    = server->connection;
 	info2.buffer_size   = server->buffer_size;
 	info2.volume_number = NCP_FINFO(inode)->volNumber;
@@ -347,21 +347,21 @@ static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg
 		{
 			u16 uid;
 
-			SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid));
+			SET_UID(uid, from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid));
 			if (put_user(uid, (u16 __user *)argp))
 				return -EFAULT;
 			return 0;
 		}
 	case NCP_IOC_GETMOUNTUID32:
 	{
-		uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+		uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 		if (put_user(uid, (u32 __user *)argp))
 			return -EFAULT;
 		return 0;
 	}
 	case NCP_IOC_GETMOUNTUID64:
 	{
-		uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid);
+		uid_t uid = from_kuid_tp_munged(current_user_ns(), server->m.mounted_uid);
 		if (put_user(uid, (u64 __user *)argp))
 			return -EFAULT;
 		return 0;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 8a4a266..c3e7ecb 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -653,14 +653,21 @@ static void posix_acl_fix_xattr_userns(
 		return;
 
 	for (end = entry + count; entry != end; entry++) {
+		/* from_k[ug]id_tp is safe here because the callers are:
+		 *  - posix_acl_fix_xattr_from_user() calls this with
+		 *    to=init_user_ns
+		 *  - posix_acl_fix_xattr_to_user() calls this with
+		 *    to=current_user_ns() and is only used in getxattr(),
+		 *    which copies the result to the caller
+		 */
 		switch(le16_to_cpu(entry->e_tag)) {
 		case ACL_USER:
 			uid = make_kuid(from, le32_to_cpu(entry->e_id));
-			entry->e_id = cpu_to_le32(from_kuid(to, uid));
+			entry->e_id = cpu_to_le32(from_kuid_tp(to, uid));
 			break;
 		case ACL_GROUP:
 			gid = make_kgid(from, le32_to_cpu(entry->e_id));
-			entry->e_id = cpu_to_le32(from_kgid(to, gid));
+			entry->e_id = cpu_to_le32(from_kgid_tp(to, gid));
 			break;
 		default:
 			break;
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 88c7de1..a827d6e 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -198,20 +198,20 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
 		"FDSize:\t%d\nGroups:\t",
 		get_task_state(p),
 		tgid, ngid, pid_nr_ns(pid, ns), ppid, tpid,
-		from_kuid_munged(user_ns, cred->uid),
-		from_kuid_munged(user_ns, cred->euid),
-		from_kuid_munged(user_ns, cred->suid),
-		from_kuid_munged(user_ns, cred->fsuid),
-		from_kgid_munged(user_ns, cred->gid),
-		from_kgid_munged(user_ns, cred->egid),
-		from_kgid_munged(user_ns, cred->sgid),
-		from_kgid_munged(user_ns, cred->fsgid),
+		from_kuid_tp_munged(user_ns, cred->uid),
+		from_kuid_tp_munged(user_ns, cred->euid),
+		from_kuid_tp_munged(user_ns, cred->suid),
+		from_kuid_tp_munged(user_ns, cred->fsuid),
+		from_kgid_tp_munged(user_ns, cred->gid),
+		from_kgid_tp_munged(user_ns, cred->egid),
+		from_kgid_tp_munged(user_ns, cred->sgid),
+		from_kgid_tp_munged(user_ns, cred->fsgid),
 		max_fds);
 
 	group_info = cred->group_info;
 	for (g = 0; g < group_info->ngroups; g++)
 		seq_printf(m, "%d ",
-			   from_kgid_munged(user_ns, GROUP_AT(group_info, g)));
+			   from_kgid_tp_munged(user_ns, GROUP_AT(group_info, g)));
 	put_cred(cred);
 
 #ifdef CONFIG_PID_NS
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a11eb71..2203b81 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1236,7 +1236,7 @@ static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
 	if (!task)
 		return -ESRCH;
 	length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
-			   from_kuid(file->f_cred->user_ns,
+			   from_kuid_tp(file->f_cred->user_ns,
 				     audit_get_loginuid(task)));
 	put_task_struct(task);
 	return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
@@ -2744,7 +2744,8 @@ static const struct file_operations proc_projid_map_operations = {
 	.release	= proc_id_map_release,
 };
 
-static int proc_setgroups_open(struct inode *inode, struct file *file)
+static int proc_nsadmin_open(struct inode *inode, struct file *file,
+	int (*show)(struct seq_file *, void *))
 {
 	struct user_namespace *ns = NULL;
 	struct task_struct *task;
@@ -2767,7 +2768,7 @@ static int proc_setgroups_open(struct inode *inode, struct file *file)
 			goto err_put_ns;
 	}
 
-	ret = single_open(file, &proc_setgroups_show, ns);
+	ret = single_open(file, show, ns);
 	if (ret)
 		goto err_put_ns;
 
@@ -2778,7 +2779,7 @@ err:
 	return ret;
 }
 
-static int proc_setgroups_release(struct inode *inode, struct file *file)
+static int proc_nsadmin_release(struct inode *inode, struct file *file)
 {
 	struct seq_file *seq = file->private_data;
 	struct user_namespace *ns = seq->private;
@@ -2787,12 +2788,30 @@ static int proc_setgroups_release(struct inode *inode, struct file *file)
 	return ret;
 }
 
+static int proc_setgroups_open(struct inode *inode, struct file *file)
+{
+	return proc_nsadmin_open(inode, file, &proc_setgroups_show);
+}
+
 static const struct file_operations proc_setgroups_operations = {
 	.open		= proc_setgroups_open,
 	.write		= proc_setgroups_write,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
-	.release	= proc_setgroups_release,
+	.release	= proc_nsadmin_release,
+};
+
+static int proc_transparent_open(struct inode *inode, struct file *file)
+{
+	return proc_nsadmin_open(inode, file, &proc_transparent_show);
+}
+
+static const struct file_operations proc_transparent_operations = {
+	.open		= proc_transparent_open,
+	.write		= proc_transparent_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= proc_nsadmin_release,
 };
 #endif /* CONFIG_USER_NS */
 
@@ -2901,6 +2920,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 	REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
 	REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
 	REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
+	REG("transparent", S_IRUGO|S_IWUSR, proc_transparent_operations),
 #endif
 #ifdef CONFIG_CHECKPOINT_RESTORE
 	REG("timers",	  S_IRUGO, proc_timers_operations),
diff --git a/fs/quota/kqid.c b/fs/quota/kqid.c
index ebc5e62..1cd3d8b 100644
--- a/fs/quota/kqid.c
+++ b/fs/quota/kqid.c
@@ -66,11 +66,15 @@ EXPORT_SYMBOL(qid_lt);
  */
 qid_t from_kqid(struct user_namespace *targ, struct kqid kqid)
 {
+	/* transparent UID/GID are okay here; this method is only
+	 * called either with targ=&init_user_ns or directly
+	 * before a copy_from_user(), with current_user_ns()
+	 */
 	switch (kqid.type) {
 	case USRQUOTA:
-		return from_kuid(targ, kqid.uid);
+		return from_kuid_tp(targ, kqid.uid);
 	case GRPQUOTA:
-		return from_kgid(targ, kqid.gid);
+		return from_kgid_tp(targ, kqid.gid);
 	case PRJQUOTA:
 		return from_kprojid(targ, kqid.projid);
 	default:
@@ -101,9 +105,9 @@ qid_t from_kqid_munged(struct user_namespace *targ, struct kqid kqid)
 {
 	switch (kqid.type) {
 	case USRQUOTA:
-		return from_kuid_munged(targ, kqid.uid);
+		return from_kuid_tp_munged(targ, kqid.uid);
 	case GRPQUOTA:
-		return from_kgid_munged(targ, kqid.gid);
+		return from_kgid_tp_munged(targ, kqid.gid);
 	case PRJQUOTA:
 		return from_kprojid_munged(targ, kqid.projid);
 	default:
diff --git a/fs/stat.c b/fs/stat.c
index bc045c7..b8cff6c 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -160,8 +160,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
 	tmp.st_nlink = stat->nlink;
 	if (tmp.st_nlink != stat->nlink)
 		return -EOVERFLOW;
-	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	tmp.st_rdev = old_encode_dev(stat->rdev);
 #if BITS_PER_LONG == 32
 	if (stat->size > MAX_NON_LFS)
@@ -246,8 +246,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
 	tmp.st_nlink = stat->nlink;
 	if (tmp.st_nlink != stat->nlink)
 		return -EOVERFLOW;
-	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
-	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	SET_UID(tmp.st_uid, from_kuid_tp_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_tp_munged(current_user_ns(), stat->gid));
 	tmp.st_rdev = encode_dev(stat->rdev);
 	tmp.st_size = stat->size;
 	tmp.st_atime = stat->atime.tv_sec;
@@ -381,8 +381,8 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
 #endif
 	tmp.st_mode = stat->mode;
 	tmp.st_nlink = stat->nlink;
-	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
-	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
+	tmp.st_uid = from_kuid_tp_munged(current_user_ns(), stat->uid);
+	tmp.st_gid = from_kgid_tp_munged(current_user_ns(), stat->gid);
 	tmp.st_atime = stat->atime.tv_sec;
 	tmp.st_atime_nsec = stat->atime.tv_nsec;
 	tmp.st_mtime = stat->mtime.tv_sec;
diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
index 0383552..bb7e2c5 100644
--- a/include/linux/uidgid.h
+++ b/include/linux/uidgid.h
@@ -124,8 +124,10 @@ extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
 
 extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
 extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
-extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
-extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
+extern uid_t from_kuid_tp(struct user_namespace *to, kuid_t uid);
+extern gid_t from_kgid_tp(struct user_namespace *to, kgid_t gid);
+extern uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t uid);
+extern gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t gid);
 
 static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
 {
@@ -159,17 +161,27 @@ static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
 	return __kgid_val(kgid);
 }
 
-static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
+static inline uid_t from_kuid_tp(struct user_namespace *to, kuid_t kuid)
 {
-	uid_t uid = from_kuid(to, kuid);
+	return __kuid_val(kuid);
+}
+
+static inline gid_t from_kgid_tp(struct user_namespace *to, kgid_t kgid)
+{
+	return __kgid_val(kgid);
+}
+
+static inline uid_t from_kuid_tp_munged(struct user_namespace *to, kuid_t kuid)
+{
+	uid_t uid = from_kuid_tp(to, kuid);
 	if (uid == (uid_t)-1)
 		uid = overflowuid;
 	return uid;
 }
 
-static inline gid_t from_kgid_munged(struct user_namespace *to, kgid_t kgid)
+static inline gid_t from_kgid_tp_munged(struct user_namespace *to, kgid_t kgid)
 {
-	gid_t gid = from_kgid(to, kgid);
+	gid_t gid = from_kgid_tp(to, kgid);
 	if (gid == (gid_t)-1)
 		gid = overflowgid;
 	return gid;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 8297e5b..18291ac 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -28,6 +28,8 @@ struct user_namespace {
 	struct uid_gid_map	projid_map;
 	atomic_t		count;
 	struct user_namespace	*parent;
+	/* self for normal ns; first opaque parent for transparent ns */
+	struct user_namespace	*opaque;
 	int			level;
 	kuid_t			owner;
 	kgid_t			group;
@@ -71,6 +73,8 @@ extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, lo
 extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_t *);
 extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t, loff_t *);
 extern int proc_setgroups_show(struct seq_file *m, void *v);
+extern ssize_t proc_transparent_write(struct file *, const char __user *, size_t, loff_t *);
+extern int proc_transparent_show(struct seq_file *m, void *v);
 extern bool userns_may_setgroups(const struct user_namespace *ns);
 #else
 
diff --git a/include/net/scm.h b/include/net/scm.h
index 59fa93c..601450a 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -121,8 +121,8 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
 		struct user_namespace *current_ns = current_user_ns();
 		struct ucred ucreds = {
 			.pid = scm->creds.pid,
-			.uid = from_kuid_munged(current_ns, scm->creds.uid),
-			.gid = from_kgid_munged(current_ns, scm->creds.gid),
+			.uid = from_kuid_tp_munged(current_ns, scm->creds.uid),
+			.gid = from_kgid_tp_munged(current_ns, scm->creds.gid),
 		};
 		put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
 	}
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index ade739f..b6de6f4 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -645,7 +645,7 @@ static void __do_notify(struct mqueue_inode_info *info)
 			rcu_read_lock();
 			sig_i.si_pid = task_tgid_nr_ns(current,
 						ns_of_pid(info->notify_owner));
-			sig_i.si_uid = from_kuid_munged(info->notify_user_ns, current_uid());
+			sig_i.si_uid = from_kuid_tp_munged(info->notify_user_ns, current_uid());
 			rcu_read_unlock();
 
 			kill_pid_info(info->notify.sigev_signo,
diff --git a/ipc/msg.c b/ipc/msg.c
index 1471db9..e49adeb 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -1052,10 +1052,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
 		   msq->q_qnum,
 		   msq->q_lspid,
 		   msq->q_lrpid,
-		   from_kuid_munged(user_ns, msq->q_perm.uid),
-		   from_kgid_munged(user_ns, msq->q_perm.gid),
-		   from_kuid_munged(user_ns, msq->q_perm.cuid),
-		   from_kgid_munged(user_ns, msq->q_perm.cgid),
+		   from_kuid_tp_munged(user_ns, msq->q_perm.uid),
+		   from_kgid_tp_munged(user_ns, msq->q_perm.gid),
+		   from_kuid_tp_munged(user_ns, msq->q_perm.cuid),
+		   from_kgid_tp_munged(user_ns, msq->q_perm.cgid),
 		   msq->q_stime,
 		   msq->q_rtime,
 		   msq->q_ctime);
diff --git a/ipc/sem.c b/ipc/sem.c
index b3757ea..99589c3 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -2208,10 +2208,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
 		   sma->sem_perm.id,
 		   sma->sem_perm.mode,
 		   sma->sem_nsems,
-		   from_kuid_munged(user_ns, sma->sem_perm.uid),
-		   from_kgid_munged(user_ns, sma->sem_perm.gid),
-		   from_kuid_munged(user_ns, sma->sem_perm.cuid),
-		   from_kgid_munged(user_ns, sma->sem_perm.cgid),
+		   from_kuid_tp_munged(user_ns, sma->sem_perm.uid),
+		   from_kgid_tp_munged(user_ns, sma->sem_perm.gid),
+		   from_kuid_tp_munged(user_ns, sma->sem_perm.cuid),
+		   from_kgid_tp_munged(user_ns, sma->sem_perm.cgid),
 		   sem_otime,
 		   sma->sem_ctime);
 
diff --git a/ipc/shm.c b/ipc/shm.c
index 1328251..599ab25 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1392,10 +1392,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
 		   shp->shm_cprid,
 		   shp->shm_lprid,
 		   shp->shm_nattch,
-		   from_kuid_munged(user_ns, shp->shm_perm.uid),
-		   from_kgid_munged(user_ns, shp->shm_perm.gid),
-		   from_kuid_munged(user_ns, shp->shm_perm.cuid),
-		   from_kgid_munged(user_ns, shp->shm_perm.cgid),
+		   from_kuid_tp_munged(user_ns, shp->shm_perm.uid),
+		   from_kgid_tp_munged(user_ns, shp->shm_perm.gid),
+		   from_kuid_tp_munged(user_ns, shp->shm_perm.cuid),
+		   from_kgid_tp_munged(user_ns, shp->shm_perm.cgid),
 		   shp->shm_atim,
 		   shp->shm_dtim,
 		   shp->shm_ctim,
diff --git a/ipc/util.c b/ipc/util.c
index 798cad1..81b39d5 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -513,10 +513,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
 void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out)
 {
 	out->key	= in->key;
-	out->uid	= from_kuid_munged(current_user_ns(), in->uid);
-	out->gid	= from_kgid_munged(current_user_ns(), in->gid);
-	out->cuid	= from_kuid_munged(current_user_ns(), in->cuid);
-	out->cgid	= from_kgid_munged(current_user_ns(), in->cgid);
+	out->uid	= from_kuid_tp_munged(current_user_ns(), in->uid);
+	out->gid	= from_kgid_tp_munged(current_user_ns(), in->gid);
+	out->cuid	= from_kuid_tp_munged(current_user_ns(), in->cuid);
+	out->cgid	= from_kgid_tp_munged(current_user_ns(), in->cgid);
 	out->mode	= in->mode;
 	out->seq	= in->seq;
 }
diff --git a/kernel/acct.c b/kernel/acct.c
index 74963d1..bbfa0b9 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -489,8 +489,8 @@ static void do_acct_process(struct bsd_acct_struct *acct)
 
 	fill_ac(&ac);
 	/* we really need to bite the bullet and change layout */
-	ac.ac_uid = from_kuid_munged(file->f_cred->user_ns, orig_cred->uid);
-	ac.ac_gid = from_kgid_munged(file->f_cred->user_ns, orig_cred->gid);
+	ac.ac_uid = from_kuid_tp_munged(file->f_cred->user_ns, orig_cred->uid);
+	ac.ac_gid = from_kgid_tp_munged(file->f_cred->user_ns, orig_cred->gid);
 #if ACCT_VERSION == 1 || ACCT_VERSION == 2
 	/* backward-compatible 16 bit fields */
 	ac.ac_uid16 = ac.ac_uid;
diff --git a/kernel/exit.c b/kernel/exit.c
index 9e6e135..207e284 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -983,7 +983,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
 {
 	int state, retval, status;
 	pid_t pid = task_pid_vnr(p);
-	uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
+	uid_t uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
 	struct siginfo __user *infop;
 
 	if (!likely(wo->wo_flags & WEXITED))
@@ -1189,7 +1189,7 @@ static int wait_task_stopped(struct wait_opts *wo,
 	if (!unlikely(wo->wo_flags & WNOWAIT))
 		*p_code = 0;
 
-	uid = from_kuid_munged(current_user_ns(), task_uid(p));
+	uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
 unlock_sig:
 	spin_unlock_irq(&p->sighand->siglock);
 	if (!exit_code)
@@ -1263,7 +1263,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
 	}
 	if (!unlikely(wo->wo_flags & WNOWAIT))
 		p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
-	uid = from_kuid_munged(current_user_ns(), task_uid(p));
+	uid = from_kuid_tp_munged(current_user_ns(), task_uid(p));
 	spin_unlock_irq(&p->sighand->siglock);
 
 	pid = task_pid_vnr(p);
diff --git a/kernel/groups.c b/kernel/groups.c
index 74d431d..ec2ecf8 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -70,7 +70,7 @@ static int groups_to_user(gid_t __user *grouplist,
 
 	for (i = 0; i < count; i++) {
 		gid_t gid;
-		gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i));
+		gid = from_kgid_tp_munged(user_ns, GROUP_AT(group_info, i));
 		if (put_user(gid, grouplist+i))
 			return -EFAULT;
 	}
diff --git a/kernel/signal.c b/kernel/signal.c
index 96e9bc4..2d7e071 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -958,7 +958,7 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str
 		return;
 
 	rcu_read_lock();
-	info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns),
+	info->si_uid = from_kuid_tp_munged(task_cred_xxx(t, user_ns),
 					make_kuid(current_user_ns(), info->si_uid));
 	rcu_read_unlock();
 }
@@ -1027,7 +1027,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
 			q->info.si_code = SI_USER;
 			q->info.si_pid = task_tgid_nr_ns(current,
 							task_active_pid_ns(t));
-			q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+			q->info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 			break;
 		case (unsigned long) SEND_SIG_PRIV:
 			q->info.si_signo = sig;
@@ -1609,7 +1609,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
 	 */
 	rcu_read_lock();
 	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent));
-	info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns),
+	info.si_uid = from_kuid_tp_munged(task_cred_xxx(tsk->parent, user_ns),
 				       task_uid(tsk));
 	rcu_read_unlock();
 
@@ -1695,7 +1695,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
 	 */
 	rcu_read_lock();
 	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(parent));
-	info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
+	info.si_uid = from_kuid_tp_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
 	rcu_read_unlock();
 
 	task_cputime(tsk, &utime, &stime);
@@ -1904,7 +1904,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
 	info.si_signo = signr;
 	info.si_code = exit_code;
 	info.si_pid = task_pid_vnr(current);
-	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 
 	/* Let the debugger run.  */
 	ptrace_stop(exit_code, why, 1, &info);
@@ -2113,7 +2113,7 @@ static int ptrace_signal(int signr, siginfo_t *info)
 		info->si_code = SI_USER;
 		rcu_read_lock();
 		info->si_pid = task_pid_vnr(current->parent);
-		info->si_uid = from_kuid_munged(current_user_ns(),
+		info->si_uid = from_kuid_tp_munged(current_user_ns(),
 						task_uid(current->parent));
 		rcu_read_unlock();
 	}
@@ -2856,7 +2856,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
 	info.si_errno = 0;
 	info.si_code = SI_USER;
 	info.si_pid = task_tgid_vnr(current);
-	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 
 	return kill_something_info(sig, &info, pid);
 }
@@ -2899,7 +2899,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
 	info.si_errno = 0;
 	info.si_code = SI_TKILL;
 	info.si_pid = task_tgid_vnr(current);
-	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+	info.si_uid = from_kuid_tp_munged(current_user_ns(), current_uid());
 
 	return do_send_specific(tgid, pid, sig, &info);
 }
diff --git a/kernel/sys.c b/kernel/sys.c
index 89d5be4..ce7833d 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -648,9 +648,9 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
 	int retval;
 	uid_t ruid, euid, suid;
 
-	ruid = from_kuid_munged(cred->user_ns, cred->uid);
-	euid = from_kuid_munged(cred->user_ns, cred->euid);
-	suid = from_kuid_munged(cred->user_ns, cred->suid);
+	ruid = from_kuid_tp_munged(cred->user_ns, cred->uid);
+	euid = from_kuid_tp_munged(cred->user_ns, cred->euid);
+	suid = from_kuid_tp_munged(cred->user_ns, cred->suid);
 
 	retval = put_user(ruid, ruidp);
 	if (!retval) {
@@ -722,9 +722,9 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
 	int retval;
 	gid_t rgid, egid, sgid;
 
-	rgid = from_kgid_munged(cred->user_ns, cred->gid);
-	egid = from_kgid_munged(cred->user_ns, cred->egid);
-	sgid = from_kgid_munged(cred->user_ns, cred->sgid);
+	rgid = from_kgid_tp_munged(cred->user_ns, cred->gid);
+	egid = from_kgid_tp_munged(cred->user_ns, cred->egid);
+	sgid = from_kgid_tp_munged(cred->user_ns, cred->sgid);
 
 	retval = put_user(rgid, rgidp);
 	if (!retval) {
@@ -751,7 +751,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
 	kuid_t kuid;
 
 	old = current_cred();
-	old_fsuid = from_kuid_munged(old->user_ns, old->fsuid);
+	old_fsuid = from_kuid_tp_munged(old->user_ns, old->fsuid);
 
 	kuid = make_kuid(old->user_ns, uid);
 	if (!uid_valid(kuid))
@@ -790,7 +790,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
 	kgid_t kgid;
 
 	old = current_cred();
-	old_fsgid = from_kgid_munged(old->user_ns, old->fsgid);
+	old_fsgid = from_kgid_tp_munged(old->user_ns, old->fsgid);
 
 	kgid = make_kgid(old->user_ns, gid);
 	if (!gid_valid(kgid))
@@ -858,25 +858,25 @@ SYSCALL_DEFINE0(getppid)
 SYSCALL_DEFINE0(getuid)
 {
 	/* Only we change this so SMP safe */
-	return from_kuid_munged(current_user_ns(), current_uid());
+	return from_kuid_tp_munged(current_user_ns(), current_uid());
 }
 
 SYSCALL_DEFINE0(geteuid)
 {
 	/* Only we change this so SMP safe */
-	return from_kuid_munged(current_user_ns(), current_euid());
+	return from_kuid_tp_munged(current_user_ns(), current_euid());
 }
 
 SYSCALL_DEFINE0(getgid)
 {
 	/* Only we change this so SMP safe */
-	return from_kgid_munged(current_user_ns(), current_gid());
+	return from_kgid_tp_munged(current_user_ns(), current_gid());
 }
 
 SYSCALL_DEFINE0(getegid)
 {
 	/* Only we change this so SMP safe */
-	return from_kgid_munged(current_user_ns(), current_egid());
+	return from_kgid_tp_munged(current_user_ns(), current_egid());
 }
 
 void do_sys_times(struct tms *tms)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8a4bd6b..d4f03ee 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -2743,7 +2743,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter)
 	seq_printf(m, "#    | task: %.16s-%d "
 		   "(uid:%d nice:%ld policy:%ld rt_prio:%ld)\n",
 		   data->comm, data->pid,
-		   from_kuid_munged(seq_user_ns(m), data->uid), data->nice,
+		   from_kuid_tp_munged(seq_user_ns(m), data->uid), data->nice,
 		   data->policy, data->rt_priority);
 	seq_puts(m, "#    -----------------\n");
 
diff --git a/kernel/tsacct.c b/kernel/tsacct.c
index f8e26ab..19e3a61 100644
--- a/kernel/tsacct.c
+++ b/kernel/tsacct.c
@@ -60,8 +60,8 @@ void bacct_add_tsk(struct user_namespace *user_ns,
 	stats->ac_pid	 = task_pid_nr_ns(tsk, pid_ns);
 	rcu_read_lock();
 	tcred = __task_cred(tsk);
-	stats->ac_uid	 = from_kuid_munged(user_ns, tcred->uid);
-	stats->ac_gid	 = from_kgid_munged(user_ns, tcred->gid);
+	stats->ac_uid	 = from_kuid_tp_munged(user_ns, tcred->uid);
+	stats->ac_gid	 = from_kgid_tp_munged(user_ns, tcred->gid);
 	stats->ac_ppid	 = pid_alive(tsk) ?
 		task_tgid_nr_ns(rcu_dereference(tsk->real_parent), pid_ns) : 0;
 	rcu_read_unlock();
diff --git a/kernel/uid16.c b/kernel/uid16.c
index d58cc4d..f91f270 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -63,9 +63,9 @@ SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euid
 	int retval;
 	old_uid_t ruid, euid, suid;
 
-	ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
-	euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
-	suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
+	ruid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->uid));
+	euid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->euid));
+	suid = high2lowuid(from_kuid_tp_munged(cred->user_ns, cred->suid));
 
 	if (!(retval   = put_user(ruid, ruidp)) &&
 	    !(retval   = put_user(euid, euidp)))
@@ -87,9 +87,9 @@ SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egid
 	int retval;
 	old_gid_t rgid, egid, sgid;
 
-	rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
-	egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
-	sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
+	rgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->gid));
+	egid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->egid));
+	sgid = high2lowgid(from_kgid_tp_munged(cred->user_ns, cred->sgid));
 
 	if (!(retval   = put_user(rgid, rgidp)) &&
 	    !(retval   = put_user(egid, egidp)))
@@ -118,7 +118,7 @@ static int groups16_to_user(old_gid_t __user *grouplist,
 
 	for (i = 0; i < group_info->ngroups; i++) {
 		kgid = GROUP_AT(group_info, i);
-		group = high2lowgid(from_kgid_munged(user_ns, kgid));
+		group = high2lowgid(from_kgid_tp_munged(user_ns, kgid));
 		if (put_user(group, grouplist+i))
 			return -EFAULT;
 	}
@@ -198,20 +198,20 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)
 
 SYSCALL_DEFINE0(getuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_uid()));
 }
 
 SYSCALL_DEFINE0(geteuid16)
 {
-	return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
+	return high2lowuid(from_kuid_tp_munged(current_user_ns(), current_euid()));
 }
 
 SYSCALL_DEFINE0(getgid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_gid()));
 }
 
 SYSCALL_DEFINE0(getegid16)
 {
-	return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
+	return high2lowgid(from_kgid_tp_munged(current_user_ns(), current_egid()));
 }
diff --git a/kernel/user.c b/kernel/user.c
index b069ccb..e1fd9e5 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -48,6 +48,7 @@ struct user_namespace init_user_ns = {
 		},
 	},
 	.count = ATOMIC_INIT(3),
+	.opaque = &init_user_ns,
 	.owner = GLOBAL_ROOT_UID,
 	.group = GLOBAL_ROOT_GID,
 	.ns.inum = PROC_USER_INIT_INO,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 9bafc21..44a7d3d 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -98,6 +98,7 @@ int create_user_ns(struct cred *new)
 	atomic_set(&ns->count, 1);
 	/* Leave the new->user_ns reference with the new user namespace. */
 	ns->parent = parent_ns;
+	ns->opaque = ns;
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
@@ -249,7 +250,8 @@ EXPORT_SYMBOL(make_kuid);
  *	@kuid: The kernel internal uid to start with.
  *
  *	Map @kuid into the user-namespace specified by @targ and
- *	return the resulting uid.
+ *	return the resulting uid. This ignores transparent user
+ *	namespaces and is therefore appropriate for security checks.
  *
  *	There is always a mapping into the initial user_namespace.
  *
@@ -263,33 +265,63 @@ uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
 EXPORT_SYMBOL(from_kuid);
 
 /**
- *	from_kuid_munged - Create a uid from a kuid user-namespace pair.
+ *	from_kuid_tp - Create a uid from a kuid user-namespace pair.
+ *	@targ: The user namespace we want a uid in.
+ *	@kuid: The kernel internal uid to start with.
+ *
+ *	Map @kuid into the user-namespace specified by @targ and
+ *	return the resulting uid.
+ *
+ *	This function is *not* appropriate for security checks because
+ *	if @targ is transparent, the mappings of an ancestor namespace
+ *	are used. If you intend to do anything with the result apart from
+ *	returning it to a process in @targ, you might want to use
+ *	from_kuid() instead.
+ *
+ *	There is always a mapping into the initial user_namespace.
+ *
+ *	If @kuid is not visible in @targ (uid_t)-1 is returned.
+ */
+uid_t from_kuid_tp(struct user_namespace *targ, kuid_t kuid)
+{
+	/* Map the uid from a global kernel uid */
+	struct user_namespace *opaque = READ_ONCE(targ->opaque);
+
+	return map_id_up(&opaque->uid_map, __kuid_val(kuid));
+}
+EXPORT_SYMBOL(from_kuid_tp);
+
+/**
+ *	from_kuid_tp_munged - Create a uid from a kuid user-namespace pair.
  *	@targ: The user namespace we want a uid in.
  *	@kuid: The kernel internal uid to start with.
  *
  *	Map @kuid into the user-namespace specified by @targ and
  *	return the resulting uid.
  *
+ *	This function is *not* appropriate for security checks; see the
+ *	comment above from_kuid_tp().
+ *
  *	There is always a mapping into the initial user_namespace.
  *
- *	Unlike from_kuid from_kuid_munged never fails and always
- *	returns a valid uid.  This makes from_kuid_munged appropriate
+ *	Unlike from_kuid_tp from_kuid_tp_munged never fails and always
+ *	returns a valid uid.  This makes from_kuid_tp_munged appropriate
  *	for use in syscalls like stat and getuid where failing the
  *	system call and failing to provide a valid uid are not an
  *	options.
  *
  *	If @kuid has no mapping in @targ overflowuid is returned.
  */
-uid_t from_kuid_munged(struct user_namespace *targ, kuid_t kuid)
+uid_t from_kuid_tp_munged(struct user_namespace *targ, kuid_t kuid)
 {
 	uid_t uid;
-	uid = from_kuid(targ, kuid);
+	uid = from_kuid_tp(targ, kuid);
 
 	if (uid == (uid_t) -1)
 		uid = overflowuid;
 	return uid;
 }
-EXPORT_SYMBOL(from_kuid_munged);
+EXPORT_SYMBOL(from_kuid_tp_munged);
 
 /**
  *	make_kgid - Map a user-namespace gid pair into a kgid.
@@ -317,7 +349,8 @@ EXPORT_SYMBOL(make_kgid);
  *	@kgid: The kernel internal gid to start with.
  *
  *	Map @kgid into the user-namespace specified by @targ and
- *	return the resulting gid.
+ *	return the resulting gid. This ignores transparent user
+ *	namespaces and is therefore appropriate for security checks.
  *
  *	There is always a mapping into the initial user_namespace.
  *
@@ -331,32 +364,62 @@ gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
 EXPORT_SYMBOL(from_kgid);
 
 /**
- *	from_kgid_munged - Create a gid from a kgid user-namespace pair.
+ *	from_kgid_tp - Create a gid from a kgid user-namespace pair.
  *	@targ: The user namespace we want a gid in.
  *	@kgid: The kernel internal gid to start with.
  *
  *	Map @kgid into the user-namespace specified by @targ and
  *	return the resulting gid.
  *
+ *	This function is *not* appropriate for security checks because
+ *	if @targ is transparent, the mappings of an ancestor namespace
+ *	are used. If you intend to do anything with the result apart from
+ *	returning it to a process in @targ, you might want to use
+ *	from_kgid() instead.
+ *
  *	There is always a mapping into the initial user_namespace.
  *
- *	Unlike from_kgid from_kgid_munged never fails and always
- *	returns a valid gid.  This makes from_kgid_munged appropriate
+ *	If @kgid is not visible in @targ (gid_t)-1 is returned.
+ */
+gid_t from_kgid_tp(struct user_namespace *targ, kgid_t kgid)
+{
+	/* Map the gid from a global kernel gid */
+	struct user_namespace *opaque = READ_ONCE(targ->opaque);
+
+	return map_id_up(&opaque->gid_map, __kgid_val(kgid));
+}
+EXPORT_SYMBOL(from_kgid_tp);
+
+/**
+ *	from_kgid_tp_munged - Create a gid from a kgid user-namespace pair.
+ *	@targ: The user namespace we want a gid in.
+ *	@kgid: The kernel internal gid to start with.
+ *
+ *	Map @kgid into the user-namespace specified by @targ and
+ *	return the resulting gid.
+ *
+ *	This function is *not* appropriate for security checks; see the
+ *	comment above from_kgid_tp().
+ *
+ *	There is always a mapping into the initial user_namespace.
+ *
+ *	Unlike from_kgid_tp from_kgid_tp_munged never fails and always
+ *	returns a valid gid.  This makes from_kgid_tp_munged appropriate
  *	for use in syscalls like stat and getgid where failing the
  *	system call and failing to provide a valid gid are not options.
  *
  *	If @kgid has no mapping in @targ overflowgid is returned.
  */
-gid_t from_kgid_munged(struct user_namespace *targ, kgid_t kgid)
+gid_t from_kgid_tp_munged(struct user_namespace *targ, kgid_t kgid)
 {
 	gid_t gid;
-	gid = from_kgid(targ, kgid);
+	gid = from_kgid_tp(targ, kgid);
 
 	if (gid == (gid_t) -1)
 		gid = overflowgid;
 	return gid;
 }
-EXPORT_SYMBOL(from_kgid_munged);
+EXPORT_SYMBOL(from_kgid_tp_munged);
 
 /**
  *	make_kprojid - Map a user-namespace projid pair into a kprojid.
@@ -811,6 +874,18 @@ static bool new_idmap_permitted(const struct file *file,
 				struct uid_gid_map *new_map)
 {
 	const struct cred *cred = file->f_cred;
+	unsigned int idx;
+
+	/* Don't allow non-identity mappings in transparent namespaces. */
+	if (ns != ns->opaque) {
+		for (idx = 0; idx < new_map->nr_extents; idx++) {
+			struct uid_gid_extent *ext = &new_map->extent[idx];
+
+			if (ext->first != ext->lower_first)
+				return false;
+		}
+	}
+
 	/* Don't allow mappings that would allow anything that wouldn't
 	 * be allowed without the establishment of unprivileged mappings.
 	 */
@@ -922,6 +997,81 @@ out_unlock:
 	goto out;
 }
 
+int proc_transparent_show(struct seq_file *seq, void *v)
+{
+	struct user_namespace *ns = seq->private;
+	struct user_namespace *opaque = READ_ONCE(ns->opaque);
+
+	seq_printf(seq, "%d\n", (ns == opaque) ? 0 : 1);
+	return 0;
+}
+
+ssize_t proc_transparent_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct seq_file *seq = file->private_data;
+	struct user_namespace *ns = seq->private;
+	char kbuf[8], *pos;
+	bool transparent;
+	ssize_t ret;
+
+	/* Only allow a very narrow range of strings to be written */
+	ret = -EINVAL;
+	if ((*ppos != 0) || (count >= sizeof(kbuf)))
+		goto out;
+
+	/* What was written? */
+	ret = -EFAULT;
+	if (copy_from_user(kbuf, buf, count))
+		goto out;
+	kbuf[count] = '\0';
+	pos = kbuf;
+
+	/* What is being requested? */
+	ret = -EINVAL;
+	if (pos[0] == '1') {
+		pos += 1;
+		transparent = true;
+	} else if (pos[0] == '0') {
+		pos += 1;
+		transparent = false;
+	} else
+		goto out;
+
+	/* Verify there is not trailing junk on the line */
+	pos = skip_spaces(pos);
+	if (*pos != '\0')
+		goto out;
+
+	ret = -EPERM;
+	mutex_lock(&userns_state_mutex);
+	/* Is the requested state different from the current one? */
+	if (transparent != (ns->opaque != ns)) {
+		/* You can't turn off transparent mode. */
+		if (!transparent)
+			goto out_unlock;
+		/* If there are existing mappings, they might be
+		 * non-identity mappings. Therefore, block transparent
+		 * mode. This also prevents making the init namespace
+		 * transparent (which wouldn't work).
+		 */
+		if (ns->uid_map.nr_extents != 0 || ns->gid_map.nr_extents != 0)
+			goto out_unlock;
+		/* Okay! Make the namespace transparent. */
+		ns->opaque = ns->parent->opaque;
+	}
+	mutex_unlock(&userns_state_mutex);
+
+	/* Report a successful write */
+	*ppos = count;
+	ret = count;
+out:
+	return ret;
+out_unlock:
+	mutex_unlock(&userns_state_mutex);
+	goto out;
+}
+
 bool userns_may_setgroups(const struct user_namespace *ns)
 {
 	bool allowed;
diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c
index af46bc4..c0f45bc 100644
--- a/net/appletalk/atalk_proc.c
+++ b/net/appletalk/atalk_proc.c
@@ -184,7 +184,7 @@ static int atalk_seq_socket_show(struct seq_file *seq, void *v)
 		   sk_wmem_alloc_get(s),
 		   sk_rmem_alloc_get(s),
 		   s->sk_state,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
 out:
 	return 0;
 }
diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
index 4ad2fb7..b28e339 100644
--- a/net/ax25/ax25_uid.c
+++ b/net/ax25/ax25_uid.c
@@ -81,7 +81,7 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
 		read_lock(&ax25_uid_lock);
 		ax25_uid_for_each(ax25_uid, &ax25_uid_list) {
 			if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
-				res = from_kuid_munged(current_user_ns(), ax25_uid->uid);
+				res = from_kuid_tp_munged(current_user_ns(), ax25_uid->uid);
 				break;
 			}
 		}
@@ -175,7 +175,7 @@ static int ax25_uid_seq_show(struct seq_file *seq, void *v)
 
 		pt = hlist_entry(v, struct ax25_uid_assoc, uid_node);
 		seq_printf(seq, "%6d %s\n",
-			from_kuid_munged(seq_user_ns(seq), pt->uid),
+			from_kuid_tp_munged(seq_user_ns(seq), pt->uid),
 			ax2asc(buf, &pt->call));
 	}
 	return 0;
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 3df7aef..5e9906b 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -625,7 +625,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
 			   atomic_read(&sk->sk_refcnt),
 			   sk_rmem_alloc_get(sk),
 			   sk_wmem_alloc_get(sk),
-			   from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
+			   from_kuid_tp(seq_user_ns(seq), sock_i_uid(sk)),
 			   sock_i_ino(sk),
 			   bt->parent? sock_i_ino(bt->parent): 0LU);
 
diff --git a/net/core/sock.c b/net/core/sock.c
index 08bf97e..1e6192d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1020,8 +1020,8 @@ static void cred_to_ucred(struct pid *pid, const struct cred *cred,
 	if (cred) {
 		struct user_namespace *current_ns = current_user_ns();
 
-		ucred->uid = from_kuid_munged(current_ns, cred->euid);
-		ucred->gid = from_kgid_munged(current_ns, cred->egid);
+		ucred->uid = from_kuid_tp_munged(current_ns, cred->euid);
+		ucred->gid = from_kgid_tp_munged(current_ns, cred->egid);
 	}
 }
 
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 25af124..26f1ec2 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -134,7 +134,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
 	}
 #endif
 
-	r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
+	r->idiag_uid = from_kuid_tp_munged(user_ns, sock_i_uid(sk));
 	r->idiag_inode = sock_i_ino(sk);
 
 	return 0;
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 66ddcb6..f3b27ea 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -1121,7 +1121,7 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
 		sk_wmem_alloc_get(sp),
 		sk_rmem_alloc_get(sp),
 		0, 0L, 0,
-		from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
+		from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
 		0, sock_i_ino(sp),
 		atomic_read(&sp->sk_refcnt), sp,
 		atomic_read(&sp->sk_drops));
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 438f50c..759095ee 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -1033,7 +1033,7 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
 		sk_wmem_alloc_get(sp),
 		sk_rmem_alloc_get(sp),
 		0, 0L, 0,
-		from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+		from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
 		0, sock_i_ino(sp),
 		atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
 }
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 1cb67de..4e19885 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -133,8 +133,8 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write,
 	};
 
 	inet_get_ping_group_range_table(table, &low, &high);
-	urange[0] = from_kgid_munged(user_ns, low);
-	urange[1] = from_kgid_munged(user_ns, high);
+	urange[0] = from_kgid_tp_munged(user_ns, low);
+	urange[1] = from_kgid_tp_munged(user_ns, high);
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0) {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 3708de2..5a5ae86 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2161,8 +2161,8 @@ static void get_openreq4(const struct request_sock *req,
 		1,    /* timers active (only the expire timer) */
 		jiffies_delta_to_clock_t(delta),
 		req->num_timeout,
-		from_kuid_munged(seq_user_ns(f),
-				 sock_i_uid(req->rsk_listener)),
+		from_kuid_tp_munged(seq_user_ns(f),
+				    sock_i_uid(req->rsk_listener)),
 		0,  /* non standard timer */
 		0, /* open_requests have no inode */
 		0,
@@ -2217,7 +2217,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
 		timer_active,
 		jiffies_delta_to_clock_t(timer_expires - jiffies),
 		icsk->icsk_retransmits,
-		from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)),
+		from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sk)),
 		icsk->icsk_probes_out,
 		sock_i_ino(sk),
 		atomic_read(&sk->sk_refcnt), sk,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 0ff31d9..e3579b2 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2408,7 +2408,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
 		sk_wmem_alloc_get(sp),
 		sk_rmem_alloc_get(sp),
 		0, 0L, 0,
-		from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
+		from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(sp)),
 		0, sock_i_ino(sp),
 		atomic_read(&sp->sk_refcnt), sp,
 		atomic_read(&sp->sk_drops));
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 37874e2..d66dd7c 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -1028,7 +1028,7 @@ void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
 		   sk_wmem_alloc_get(sp),
 		   sk_rmem_alloc_get(sp),
 		   0, 0L, 0,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
 		   0,
 		   sock_i_ino(sp),
 		   atomic_read(&sp->sk_refcnt), sp,
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index b912f0d..a1c7516 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -789,7 +789,7 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v)
 			   ((fl->share == IPV6_FL_S_PROCESS) ?
 			    pid_nr_ns(fl->owner.pid, state->pid_ns) :
 			    ((fl->share == IPV6_FL_S_USER) ?
-			     from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
+			     from_kuid_tp_munged(seq_user_ns(seq), fl->owner.uid) :
 			     0)),
 			   atomic_read(&fl->users),
 			   fl->linger/HZ,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f36c2d0..04643ca 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1696,8 +1696,8 @@ static void get_openreq6(struct seq_file *seq,
 		   1,   /* timers active (only the expire timer) */
 		   jiffies_to_clock_t(ttd),
 		   req->num_timeout,
-		   from_kuid_munged(seq_user_ns(seq),
-				    sock_i_uid(req->rsk_listener)),
+		   from_kuid_tp_munged(seq_user_ns(seq),
+				       sock_i_uid(req->rsk_listener)),
 		   0,  /* non standard timer */
 		   0, /* open_requests have no inode */
 		   0, req);
@@ -1760,7 +1760,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
 		   timer_active,
 		   jiffies_delta_to_clock_t(timer_expires - jiffies),
 		   icsk->icsk_retransmits,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sp)),
 		   icsk->icsk_probes_out,
 		   sock_i_ino(sp),
 		   atomic_read(&sp->sk_refcnt), sp,
diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
index c1d247e..fc1d1fe 100644
--- a/net/ipx/ipx_proc.c
+++ b/net/ipx/ipx_proc.c
@@ -217,7 +217,7 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v)
 		   sk_wmem_alloc_get(s),
 		   sk_rmem_alloc_get(s),
 		   s->sk_state,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)));
 out:
 	return 0;
 }
diff --git a/net/key/af_key.c b/net/key/af_key.c
index f9c9ecb..b76105f 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3714,7 +3714,7 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
 			       atomic_read(&s->sk_refcnt),
 			       sk_rmem_alloc_get(s),
 			       sk_wmem_alloc_get(s),
-			       from_kuid_munged(seq_user_ns(f), sock_i_uid(s)),
+			       from_kuid_tp_munged(seq_user_ns(f), sock_i_uid(s)),
 			       sock_i_ino(s)
 			       );
 	return 0;
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index 29c509c..96d2dff 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -151,7 +151,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v)
 		   sk_wmem_alloc_get(sk),
 		   sk_rmem_alloc_get(sk) - llc->copied_seq,
 		   sk->sk_state,
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 		   llc->link);
 out:
 	return 0;
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 11f81c8..3263bca 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -552,8 +552,8 @@ __build_packet_message(struct nfnl_log_net *log,
 			struct file *file = sk->sk_socket->file;
 			const struct cred *cred = file->f_cred;
 			struct user_namespace *user_ns = inst->peer_user_ns;
-			__be32 uid = htonl(from_kuid_munged(user_ns, cred->fsuid));
-			__be32 gid = htonl(from_kgid_munged(user_ns, cred->fsgid));
+			__be32 uid = htonl(from_kuid_tp_munged(user_ns, cred->fsuid));
+			__be32 gid = htonl(from_kgid_tp_munged(user_ns, cred->fsgid));
 			read_unlock_bh(&sk->sk_callback_lock);
 			if (nla_put_be32(inst->skb, NFULA_UID, uid) ||
 			    nla_put_be32(inst->skb, NFULA_GID, gid))
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 9bff6ef..21443c8 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4497,7 +4497,7 @@ static int packet_seq_show(struct seq_file *seq, void *v)
 			   po->ifindex,
 			   po->running,
 			   atomic_read(&s->sk_rmem_alloc),
-			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)),
+			   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(s)),
 			   sock_i_ino(s));
 	}
 
diff --git a/net/packet/diag.c b/net/packet/diag.c
index 0ed68f0..40b8df7 100644
--- a/net/packet/diag.c
+++ b/net/packet/diag.c
@@ -153,7 +153,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
 
 	if ((req->pdiag_show & PACKET_SHOW_INFO) &&
 	    nla_put_u32(skb, PACKET_DIAG_UID,
-			from_kuid_munged(user_ns, sock_i_uid(sk))))
+			from_kuid_tp_munged(user_ns, sock_i_uid(sk))))
 		goto out_nlmsg_trim;
 
 	if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index ffd5f22..fa90d85 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -610,7 +610,7 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v)
 			sk->sk_protocol, pn->sobject, pn->dobject,
 			pn->resource, sk->sk_state,
 			sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
-			from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+			from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 			sock_i_ino(sk),
 			atomic_read(&sk->sk_refcnt), sk,
 			atomic_read(&sk->sk_drops));
@@ -795,7 +795,7 @@ static int pn_res_seq_show(struct seq_file *seq, void *v)
 
 		seq_printf(seq, "%02X %5u %lu",
 			   (int) (psk - pnres.sk),
-			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+			   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 			   sock_i_ino(sk));
 	}
 	seq_pad(seq, '\n');
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 4cb5aed..a893492 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -224,7 +224,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
 		seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5u %5lu ", ep, sk,
 			   sctp_sk(sk)->type, sk->sk_state, hash,
 			   epb->bind_addr.port,
-			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+			   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 			   sock_i_ino(sk));
 
 		sctp_seq_dump_local_addrs(seq, epb);
@@ -346,7 +346,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
 		   assoc->assoc_id,
 		   assoc->sndbuf_used,
 		   atomic_read(&assoc->rmem_alloc),
-		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+		   from_kuid_tp_munged(seq_user_ns(seq), sock_i_uid(sk)),
 		   sock_i_ino(sk),
 		   epb->bind_addr.port,
 		   assoc->peer.port);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index dfacdc9..ce7a1ce 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -562,9 +562,9 @@ static int unix_gid_show(struct seq_file *m,
 	else
 		glen = 0;
 
-	seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
+	seq_printf(m, "%u %d:", from_kuid_tp_munged(user_ns, ug->uid), glen);
 	for (i = 0; i < glen; i++)
-		seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i)));
+		seq_printf(m, " %d", from_kgid_tp_munged(user_ns, GROUP_AT(ug->gi, i)));
 	seq_printf(m, "\n");
 	return 0;
 }
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index d580ad0..b88f73d 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -619,8 +619,8 @@ okay:
 	infobuf = kasprintf(GFP_KERNEL,
 			    "%s;%d;%d;%08x;",
 			    key->type->name,
-			    from_kuid_munged(current_user_ns(), key->uid),
-			    from_kgid_munged(current_user_ns(), key->gid),
+			    from_kuid_tp_munged(current_user_ns(), key->uid),
+			    from_kgid_tp_munged(current_user_ns(), key->gid),
 			    key->perm);
 	if (!infobuf)
 		goto error2;
diff --git a/security/keys/proc.c b/security/keys/proc.c
index f0611a6..f71449d 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -255,8 +255,8 @@ static int proc_keys_show(struct seq_file *m, void *v)
 		   atomic_read(&key->usage),
 		   xbuf,
 		   key->perm,
-		   from_kuid_munged(seq_user_ns(m), key->uid),
-		   from_kgid_munged(seq_user_ns(m), key->gid),
+		   from_kuid_tp_munged(seq_user_ns(m), key->uid),
+		   from_kgid_tp_munged(seq_user_ns(m), key->gid),
 		   key->type->name);
 
 #undef showflag
@@ -339,7 +339,7 @@ static int proc_key_users_show(struct seq_file *m, void *v)
 		key_quota_root_maxbytes : key_quota_maxbytes;
 
 	seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
-		   from_kuid_munged(seq_user_ns(m), user->uid),
+		   from_kuid_tp_munged(seq_user_ns(m), user->uid),
 		   atomic_read(&user->usage),
 		   atomic_read(&user->nkeys),
 		   atomic_read(&user->nikeys),
-- 
2.8.0.rc3.226.g39d4020

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ