Commit 99214f67 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull tracing fixes from Steven Rostedt:

 - Add missing LOCKDOWN checks for eventfs callers

   When LOCKDOWN is active for tracing, it causes inconsistent state
   when some functions succeed and others fail.

 - Use dput() to free the top level eventfs descriptor

   There was a race between accesses and freeing it.

 - Fix a long standing bug that eventfs exposed due to changing timings
   by dynamically creating files. That is, If a event file is opened for
   an instance, there's nothing preventing the instance from being
   removed which will make accessing the files cause use-after-free
   bugs.

 - Fix a ring buffer race that happens when iterating over the ring
   buffer while writers are active. Check to make sure not to read the
   event meta data if it's beyond the end of the ring buffer sub buffer.

 - Fix the print trigger that disappeared because the test to create it
   was looking for the event dir field being filled, but now it has the
   "ef" field filled for the eventfs structure.

 - Remove the unused "dir" field from the event structure.

 - Fix the order of the trace_dynamic_info as it had it backwards for
   the offset and len fields for which one was for which endianess.

 - Fix NULL pointer dereference with eventfs_remove_rec()

   If an allocation fails in one of the eventfs_add_*() functions, the
   caller of it in event_subsystem_dir() or event_create_dir() assigns
   the result to the structure. But it's assigning the ERR_PTR and not
   NULL. This was passed to eventfs_remove_rec() which expects either a
   good pointer or a NULL, not ERR_PTR. The fix is to not assign the
   ERR_PTR to the structure, but to keep it NULL on error.

 - Fix list_for_each_rcu() to use list_for_each_srcu() in
   dcache_dir_open_wrapper(). One iteration of the code used RCU but
   because it had to call sleepable code, it had to be changed to use
   SRCU, but one of the iterations was missed.

 - Fix synthetic event print function to use "as_u64" instead of passing
   in a pointer to the union. To fix big/little endian issues, the u64
   that represented several types was turned into a union to define the
   types properly.

* tag 'trace-v6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  eventfs: Fix the NULL pointer dereference bug in eventfs_remove_rec()
  tracefs/eventfs: Use list_for_each_srcu() in dcache_dir_open_wrapper()
  tracing/synthetic: Print out u64 values properly
  tracing/synthetic: Fix order of struct trace_dynamic_info
  selftests/ftrace: Fix dependencies for some of the synthetic event tests
  tracing: Remove unused trace_event_file dir field
  tracing: Use the new eventfs descriptor for print trigger
  ring-buffer: Do not attempt to read past "commit"
  tracefs/eventfs: Free top level files on removal
  ring-buffer: Avoid softlockup in ring_buffer_resize()
  tracing: Have event inject files inc the trace array ref count
  tracing: Have option files inc the trace array ref count
  tracing: Have current_trace inc the trace array ref count
  tracing: Have tracing_max_latency inc the trace array ref count
  tracing: Increase trace array ref count on enable and filter files
  tracefs/eventfs: Use dput to free the toplevel events directory
  tracefs/eventfs: Add missing lockdown checks
  tracefs: Add missing lockdown check to tracefs_create_dir()
parents 3669558b c8414dab
Loading
Loading
Loading
Loading
+52 −7
Original line number Diff line number Diff line
@@ -185,17 +185,49 @@ static struct dentry *create_dir(const char *name, struct dentry *parent, void *

/**
 * eventfs_set_ef_status_free - set the ef->status to free
 * @ti: the tracefs_inode of the dentry
 * @dentry: dentry who's status to be freed
 *
 * eventfs_set_ef_status_free will be called if no more
 * references remain
 */
void eventfs_set_ef_status_free(struct dentry *dentry)
void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry)
{
	struct tracefs_inode *ti_parent;
	struct eventfs_file *ef;
	struct eventfs_inode *ei;
	struct eventfs_file *ef, *tmp;

	/* The top level events directory may be freed by this */
	if (unlikely(ti->flags & TRACEFS_EVENT_TOP_INODE)) {
		LIST_HEAD(ef_del_list);

		mutex_lock(&eventfs_mutex);

		ei = ti->private;

		/* Record all the top level files */
		list_for_each_entry_srcu(ef, &ei->e_top_files, list,
					 lockdep_is_held(&eventfs_mutex)) {
			list_add_tail(&ef->del_list, &ef_del_list);
		}

		/* Nothing should access this, but just in case! */
		ti->private = NULL;

		mutex_unlock(&eventfs_mutex);

		/* Now safely free the top level files and their children */
		list_for_each_entry_safe(ef, tmp, &ef_del_list, del_list) {
			list_del(&ef->del_list);
			eventfs_remove(ef);
		}

		kfree(ei);
		return;
	}

	mutex_lock(&eventfs_mutex);

	ti_parent = get_tracefs(dentry->d_parent->d_inode);
	if (!ti_parent || !(ti_parent->flags & TRACEFS_EVENT_INODE))
		goto out;
@@ -420,7 +452,8 @@ static int dcache_dir_open_wrapper(struct inode *inode, struct file *file)

	ei = ti->private;
	idx = srcu_read_lock(&eventfs_srcu);
	list_for_each_entry_rcu(ef, &ei->e_top_files, list) {
	list_for_each_entry_srcu(ef, &ei->e_top_files, list,
				 srcu_read_lock_held(&eventfs_srcu)) {
		create_dentry(ef, dentry, false);
	}
	srcu_read_unlock(&eventfs_srcu, idx);
@@ -491,6 +524,9 @@ struct dentry *eventfs_create_events_dir(const char *name,
	struct tracefs_inode *ti;
	struct inode *inode;

	if (security_locked_down(LOCKDOWN_TRACEFS))
		return NULL;

	if (IS_ERR(dentry))
		return dentry;

@@ -507,7 +543,7 @@ struct dentry *eventfs_create_events_dir(const char *name,
	INIT_LIST_HEAD(&ei->e_top_files);

	ti = get_tracefs(inode);
	ti->flags |= TRACEFS_EVENT_INODE;
	ti->flags |= TRACEFS_EVENT_INODE | TRACEFS_EVENT_TOP_INODE;
	ti->private = ei;

	inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
@@ -538,6 +574,9 @@ struct eventfs_file *eventfs_add_subsystem_dir(const char *name,
	struct eventfs_inode *ei_parent;
	struct eventfs_file *ef;

	if (security_locked_down(LOCKDOWN_TRACEFS))
		return NULL;

	if (!parent)
		return ERR_PTR(-EINVAL);

@@ -569,6 +608,9 @@ struct eventfs_file *eventfs_add_dir(const char *name,
{
	struct eventfs_file *ef;

	if (security_locked_down(LOCKDOWN_TRACEFS))
		return NULL;

	if (!ef_parent)
		return ERR_PTR(-EINVAL);

@@ -606,6 +648,9 @@ int eventfs_add_events_file(const char *name, umode_t mode,
	struct eventfs_inode *ei;
	struct eventfs_file *ef;

	if (security_locked_down(LOCKDOWN_TRACEFS))
		return -ENODEV;

	if (!parent)
		return -EINVAL;

@@ -654,6 +699,9 @@ int eventfs_add_file(const char *name, umode_t mode,
{
	struct eventfs_file *ef;

	if (security_locked_down(LOCKDOWN_TRACEFS))
		return -ENODEV;

	if (!ef_parent)
		return -EINVAL;

@@ -791,7 +839,6 @@ void eventfs_remove(struct eventfs_file *ef)
void eventfs_remove_events_dir(struct dentry *dentry)
{
	struct tracefs_inode *ti;
	struct eventfs_inode *ei;

	if (!dentry || !dentry->d_inode)
		return;
@@ -800,8 +847,6 @@ void eventfs_remove_events_dir(struct dentry *dentry)
	if (!ti || !(ti->flags & TRACEFS_EVENT_INODE))
		return;

	ei = ti->private;
	d_invalidate(dentry);
	dput(dentry);
	kfree(ei);
}
+4 −1
Original line number Diff line number Diff line
@@ -385,7 +385,7 @@ static void tracefs_dentry_iput(struct dentry *dentry, struct inode *inode)

	ti = get_tracefs(inode);
	if (ti && ti->flags & TRACEFS_EVENT_INODE)
		eventfs_set_ef_status_free(dentry);
		eventfs_set_ef_status_free(ti, dentry);
	iput(inode);
}

@@ -673,6 +673,9 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent,
 */
struct dentry *tracefs_create_dir(const char *name, struct dentry *parent)
{
	if (security_locked_down(LOCKDOWN_TRACEFS))
		return NULL;

	return __create_dir(name, parent, &simple_dir_inode_operations);
}

+3 −2
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@

enum {
	TRACEFS_EVENT_INODE		= BIT(1),
	TRACEFS_EVENT_TOP_INODE		= BIT(2),
};

struct tracefs_inode {
@@ -24,6 +25,6 @@ struct inode *tracefs_get_inode(struct super_block *sb);
struct dentry *eventfs_start_creating(const char *name, struct dentry *parent);
struct dentry *eventfs_failed_creating(struct dentry *dentry);
struct dentry *eventfs_end_creating(struct dentry *dentry);
void eventfs_set_ef_status_free(struct dentry *dentry);
void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry);

#endif /* _TRACEFS_INTERNAL_H */
+3 −4
Original line number Diff line number Diff line
@@ -62,13 +62,13 @@ void trace_event_printf(struct trace_iterator *iter, const char *fmt, ...);
/* Used to find the offset and length of dynamic fields in trace events */
struct trace_dynamic_info {
#ifdef CONFIG_CPU_BIG_ENDIAN
	u16	offset;
	u16	len;
	u16	offset;
#else
	u16	len;
	u16	offset;
	u16	len;
#endif
};
} __packed;

/*
 * The trace entry - the most basic unit of tracing. This is what
@@ -650,7 +650,6 @@ struct trace_event_file {
	struct trace_event_call		*event_call;
	struct event_filter __rcu	*filter;
	struct eventfs_file             *ef;
	struct dentry			*dir;
	struct trace_array		*tr;
	struct trace_subsystem_dir	*system;
	struct list_head		triggers;
+7 −0
Original line number Diff line number Diff line
@@ -2198,6 +2198,8 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
				err = -ENOMEM;
				goto out_err;
			}

			cond_resched();
		}

		cpus_read_lock();
@@ -2388,6 +2390,11 @@ rb_iter_head_event(struct ring_buffer_iter *iter)
	 */
	commit = rb_page_commit(iter_head_page);
	smp_rmb();

	/* An event needs to be at least 8 bytes in size */
	if (iter->head > commit - 8)
		goto reset;

	event = __rb_page_index(iter_head_page, iter->head);
	length = rb_event_length(event);

Loading