Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2017-07-20 15:08:21 +0200
committerTakashi Iwai <tiwai@suse.de>2017-07-27 17:07:08 +0200
commiteba0211db9f6ab6b7609550218c7c0f6b010699d (patch)
tree5aed73b2c9dd22e6b9c1cf507816f7b496bcb18f
parent561ddb1110dd0c7f5f8cb9cb6760f83bc9c09b4f (diff)
dentry name snapshots (CVE-2017-7533 bsc#1049483).rpm-4.4.74-92.32
-rw-r--r--patches.fixes/dentry-name-snapshots.patch231
-rw-r--r--series.conf1
2 files changed, 232 insertions, 0 deletions
diff --git a/patches.fixes/dentry-name-snapshots.patch b/patches.fixes/dentry-name-snapshots.patch
new file mode 100644
index 0000000000..b9028ae641
--- /dev/null
+++ b/patches.fixes/dentry-name-snapshots.patch
@@ -0,0 +1,231 @@
+From 49d31c2f389acfe83417083e1208422b4091cd9e Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Fri, 7 Jul 2017 14:51:19 -0400
+Subject: [PATCH] dentry name snapshots
+Git-commit: 49d31c2f389acfe83417083e1208422b4091cd9e
+Patch-mainline: v4.13-rc1
+References: CVE-2017-7533 bsc#1049483
+
+take_dentry_name_snapshot() takes a safe snapshot of dentry name;
+if the name is a short one, it gets copied into caller-supplied
+structure, otherwise an extra reference to external name is grabbed
+(those are never modified). In either case the pointer to stable
+string is stored into the same structure.
+
+dentry must be held by the caller of take_dentry_name_snapshot(),
+but may be freely dropped afterwards - the snapshot will stay
+until destroyed by release_dentry_name_snapshot().
+
+Intended use:
+ struct name_snapshot s;
+
+ take_dentry_name_snapshot(&s, dentry);
+ ...
+ access s.name
+ ...
+ release_dentry_name_snapshot(&s);
+
+Replaces fsnotify_oldname_...(), gets used in fsnotify to obtain the name
+to pass down with event.
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Acked-by: Jan Kara <jack@suse.cz>
+
+---
+ fs/dcache.c | 27 +++++++++++++++++++++++++++
+ fs/debugfs/inode.c | 10 +++++-----
+ fs/namei.c | 8 ++++----
+ fs/notify/fsnotify.c | 8 ++++++--
+ include/linux/dcache.h | 6 ++++++
+ include/linux/fsnotify.h | 31 -------------------------------
+ 6 files changed, 48 insertions(+), 42 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -271,6 +271,33 @@ static inline int dname_external(const s
+ return dentry->d_name.name != dentry->d_iname;
+ }
+
++void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
++{
++ spin_lock(&dentry->d_lock);
++ if (unlikely(dname_external(dentry))) {
++ struct external_name *p = external_name(dentry);
++ atomic_inc(&p->u.count);
++ spin_unlock(&dentry->d_lock);
++ name->name = p->name;
++ } else {
++ memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
++ spin_unlock(&dentry->d_lock);
++ name->name = name->inline_name;
++ }
++}
++EXPORT_SYMBOL(take_dentry_name_snapshot);
++
++void release_dentry_name_snapshot(struct name_snapshot *name)
++{
++ if (unlikely(name->name != name->inline_name)) {
++ struct external_name *p;
++ p = container_of(name->name, struct external_name, name[0]);
++ if (unlikely(atomic_dec_and_test(&p->u.count)))
++ kfree_rcu(p, u.head);
++ }
++}
++EXPORT_SYMBOL(release_dentry_name_snapshot);
++
+ static inline void __d_set_inode_and_type(struct dentry *dentry,
+ struct inode *inode,
+ unsigned type_flags)
+--- a/fs/debugfs/inode.c
++++ b/fs/debugfs/inode.c
+@@ -669,7 +669,7 @@ struct dentry *debugfs_rename(struct den
+ {
+ int error;
+ struct dentry *dentry = NULL, *trap;
+- const char *old_name;
++ struct name_snapshot old_name;
+
+ trap = lock_rename(new_dir, old_dir);
+ /* Source or destination directories don't exist? */
+@@ -684,19 +684,19 @@ struct dentry *debugfs_rename(struct den
+ if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
+ goto exit;
+
+- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
++ take_dentry_name_snapshot(&old_name, old_dentry);
+
+ error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
+ dentry);
+ if (error) {
+- fsnotify_oldname_free(old_name);
++ release_dentry_name_snapshot(&old_name);
+ goto exit;
+ }
+ d_move(old_dentry, dentry);
+- fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
++ fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
+ d_is_dir(old_dentry),
+ NULL, old_dentry);
+- fsnotify_oldname_free(old_name);
++ release_dentry_name_snapshot(&old_name);
+ unlock_rename(new_dir, old_dir);
+ dput(dentry);
+ return old_dentry;
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -4179,11 +4179,11 @@ int vfs_rename(struct inode *old_dir, st
+ {
+ int error;
+ bool is_dir = d_is_dir(old_dentry);
+- const unsigned char *old_name;
+ struct inode *source = old_dentry->d_inode;
+ struct inode *target = new_dentry->d_inode;
+ bool new_is_dir = false;
+ unsigned max_links = new_dir->i_sb->s_max_links;
++ struct name_snapshot old_name;
+
+ /*
+ * Check source == target.
+@@ -4237,7 +4237,7 @@ int vfs_rename(struct inode *old_dir, st
+ if (error)
+ return error;
+
+- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
++ take_dentry_name_snapshot(&old_name, old_dentry);
+ dget(new_dentry);
+ if (!is_dir || (flags & RENAME_EXCHANGE))
+ lock_two_nondirectories(source, target);
+@@ -4298,14 +4298,14 @@ out:
+ mutex_unlock(&target->i_mutex);
+ dput(new_dentry);
+ if (!error) {
+- fsnotify_move(old_dir, new_dir, old_name, is_dir,
++ fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
+ !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
+ if (flags & RENAME_EXCHANGE) {
+ fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
+ new_is_dir, NULL, new_dentry);
+ }
+ }
+- fsnotify_oldname_free(old_name);
++ release_dentry_name_snapshot(&old_name);
+
+ return error;
+ }
+--- a/fs/notify/fsnotify.c
++++ b/fs/notify/fsnotify.c
+@@ -104,16 +104,20 @@ int __fsnotify_parent(struct path *path,
+ if (unlikely(!fsnotify_inode_watches_children(p_inode)))
+ __fsnotify_update_child_dentry_flags(p_inode);
+ else if (p_inode->i_fsnotify_mask & mask) {
++ struct name_snapshot name;
++
+ /* we are notifying a parent so come up with the new mask which
+ * specifies these are events which came from a child. */
+ mask |= FS_EVENT_ON_CHILD;
+
++ take_dentry_name_snapshot(&name, dentry);
+ if (path)
+ ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
+- dentry->d_name.name, 0);
++ name.name, 0);
+ else
+ ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
+- dentry->d_name.name, 0);
++ name.name, 0);
++ release_dentry_name_snapshot(&name);
+ }
+
+ dput(parent);
+--- a/include/linux/dcache.h
++++ b/include/linux/dcache.h
+@@ -615,5 +615,11 @@ static inline struct inode *d_real_inode
+ return d_backing_inode(d_real(dentry));
+ }
+
++struct name_snapshot {
++ const char *name;
++ char inline_name[DNAME_INLINE_LEN];
++};
++void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
++void release_dentry_name_snapshot(struct name_snapshot *);
+
+ #endif /* __LINUX_DCACHE_H */
+--- a/include/linux/fsnotify.h
++++ b/include/linux/fsnotify.h
+@@ -310,35 +310,4 @@ static inline void fsnotify_change(struc
+ }
+ }
+
+-#if defined(CONFIG_FSNOTIFY) /* notify helpers */
+-
+-/*
+- * fsnotify_oldname_init - save off the old filename before we change it
+- */
+-static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
+-{
+- return kstrdup(name, GFP_KERNEL);
+-}
+-
+-/*
+- * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
+- */
+-static inline void fsnotify_oldname_free(const unsigned char *old_name)
+-{
+- kfree(old_name);
+-}
+-
+-#else /* CONFIG_FSNOTIFY */
+-
+-static inline const char *fsnotify_oldname_init(const unsigned char *name)
+-{
+- return NULL;
+-}
+-
+-static inline void fsnotify_oldname_free(const unsigned char *old_name)
+-{
+-}
+-
+-#endif /* CONFIG_FSNOTIFY */
+-
+ #endif /* _LINUX_FS_NOTIFY_H */
diff --git a/series.conf b/series.conf
index 785a8d3bd0..c2dfa63302 100644
--- a/series.conf
+++ b/series.conf
@@ -2882,6 +2882,7 @@
patches.suse/squashfs-3.4.patch
patches.suse/squashfs3-properly-handle-dir_emit-failures.patch
patches.fixes/f2fs-fix-bad-prefetchw-of-NULL-page.patch
+ patches.fixes/dentry-name-snapshots.patch
########################################################
# Overlayfs