 fs/proc/fd.c | 34 +++++++++++++++++++++++++++++-----
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 0ff80f9b930f..7a97dfbb4217 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -137,6 +137,33 @@ static const struct dentry_operations tid_fd_dentry_operations = {
 	.d_delete	= pid_delete_dentry,
 };
 
+static inline int match_cred(const struct cred *a, const struct cred *b)
+{
+	return a->fsuid == b->fsuid && a->fsgid == b->fsgid;
+}
+
+/*
+ * To get here, we have ptrace() access to the task in question.
+ *
+ * But we still require that the file itself was either opened by that
+ * task or by ourselves, or that we have path search capability.
+ */
+static int get_fd_path(struct task_struct *task, struct file *file, struct path *path)
+{
+	if (!match_cred(file->f_cred, current_cred())) {
+		int match;
+
+		rcu_read_lock();
+		match = match_cred(file->f_cred, __task_cred(task));
+		rcu_read_unlock();
+		if (!match && !capable(CAP_DAC_READ_SEARCH))
+			return -EACCES;
+	}
+	*path = file->f_path;
+	path_get(&file->f_path);
+	return 0;
+}
+
 static int proc_fd_link(struct dentry *dentry, struct path *path)
 {
 	struct files_struct *files = NULL;
@@ -155,11 +182,8 @@ static int proc_fd_link(struct dentry *dentry, struct path *path)
 
 		spin_lock(&files->file_lock);
 		fd_file = fcheck_files(files, fd);
-		if (fd_file) {
-			*path = fd_file->f_path;
-			path_get(&fd_file->f_path);
-			ret = 0;
-		}
+		if (fd_file)
+			ret = get_fd_path(task, fd_file, path);
 		spin_unlock(&files->file_lock);
 		put_files_struct(files);
 	}
