Unverified Commit 74286da1 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!15167 Backport tracing fix patch

Merge Pull Request from: @ci-robot 
 
PR sync from: Li Huafei <lihuafei1@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/QZJKEHLWRXGKO5HLWUYF5YILLRW5BHWG/ 
Luo Gengkun (1):
  trace: Fix kabi breakage for trace_event_fields

Steven Rostedt (1):
  tracing: Check "%s" dereference via the field and not the TP_printk
    format


-- 
2.25.1
 
https://gitee.com/src-openeuler/kernel/issues/IBJ835 
 
Link:https://gitee.com/openeuler/kernel/pulls/15167

 

Reviewed-by: default avatarYe Weihua <yeweihua4@huawei.com>
Reviewed-by: default avatarXu Kuohai <xukuohai@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 54c95034 2da72dd5
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -283,6 +283,7 @@ struct trace_event_fields {
			const int  is_signed;
			const int  filter_type;
			const int  len;
			KABI_FILL_HOLE(unsigned int needs_test:1)
		};
		int (*define_fields)(struct trace_event_call *);
	};
@@ -332,6 +333,7 @@ enum {
	TRACE_EVENT_FL_EPROBE_BIT,
	TRACE_EVENT_FL_FPROBE_BIT,
	TRACE_EVENT_FL_CUSTOM_BIT,
	TRACE_EVENT_FL_TEST_STR_BIT,
};

/*
@@ -349,6 +351,7 @@ enum {
 *  CUSTOM        - Event is a custom event (to be attached to an exsiting tracepoint)
 *                   This is set when the custom event has not been attached
 *                   to a tracepoint yet, then it is cleared when it is.
 *  TEST_STR      - The event has a "%s" that points to a string outside the event
 */
enum {
	TRACE_EVENT_FL_FILTERED		= (1 << TRACE_EVENT_FL_FILTERED_BIT),
@@ -362,6 +365,7 @@ enum {
	TRACE_EVENT_FL_EPROBE		= (1 << TRACE_EVENT_FL_EPROBE_BIT),
	TRACE_EVENT_FL_FPROBE		= (1 << TRACE_EVENT_FL_FPROBE_BIT),
	TRACE_EVENT_FL_CUSTOM		= (1 << TRACE_EVENT_FL_CUSTOM_BIT),
	TRACE_EVENT_FL_TEST_STR		= (1 << TRACE_EVENT_FL_TEST_STR_BIT),
};

#define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE)
+56 −167
Original line number Diff line number Diff line
@@ -3760,17 +3760,12 @@ char *trace_iter_expand_format(struct trace_iterator *iter)
}

/* Returns true if the string is safe to dereference from an event */
static bool trace_safe_str(struct trace_iterator *iter, const char *str,
			   bool star, int len)
static bool trace_safe_str(struct trace_iterator *iter, const char *str)
{
	unsigned long addr = (unsigned long)str;
	struct trace_event *trace_event;
	struct trace_event_call *event;

	/* Ignore strings with no length */
	if (star && !len)
		return true;

	/* OK if part of the event data */
	if ((addr >= (unsigned long)iter->ent) &&
	    (addr < (unsigned long)iter->ent + iter->ent_size))
@@ -3810,142 +3805,69 @@ static bool trace_safe_str(struct trace_iterator *iter, const char *str,
	return false;
}

static const char *show_buffer(struct trace_seq *s)
{
	struct seq_buf *seq = &s->seq;

	seq_buf_terminate(seq);

	return seq->buffer;
}

static DEFINE_STATIC_KEY_FALSE(trace_no_verify);

static int test_can_verify_check(const char *fmt, ...)
{
	char buf[16];
	va_list ap;
	int ret;

	/*
	 * The verifier is dependent on vsnprintf() modifies the va_list
	 * passed to it, where it is sent as a reference. Some architectures
	 * (like x86_32) passes it by value, which means that vsnprintf()
	 * does not modify the va_list passed to it, and the verifier
	 * would then need to be able to understand all the values that
	 * vsnprintf can use. If it is passed by value, then the verifier
	 * is disabled.
	 */
	va_start(ap, fmt);
	vsnprintf(buf, 16, "%d", ap);
	ret = va_arg(ap, int);
	va_end(ap);

	return ret;
}

static void test_can_verify(void)
{
	if (!test_can_verify_check("%d %d", 0, 1)) {
		pr_info("trace event string verifier disabled\n");
		static_branch_inc(&trace_no_verify);
	}
}

/**
 * trace_check_vprintf - Check dereferenced strings while writing to the seq buffer
 * ignore_event - Check dereferenced fields while writing to the seq buffer
 * @iter: The iterator that holds the seq buffer and the event being printed
 * @fmt: The format used to print the event
 * @ap: The va_list holding the data to print from @fmt.
 *
 * This writes the data into the @iter->seq buffer using the data from
 * @fmt and @ap. If the format has a %s, then the source of the string
 * is examined to make sure it is safe to print, otherwise it will
 * warn and print "[UNSAFE MEMORY]" in place of the dereferenced string
 * pointer.
 * At boot up, test_event_printk() will flag any event that dereferences
 * a string with "%s" that does exist in the ring buffer. It may still
 * be valid, as the string may point to a static string in the kernel
 * rodata that never gets freed. But if the string pointer is pointing
 * to something that was allocated, there's a chance that it can be freed
 * by the time the user reads the trace. This would cause a bad memory
 * access by the kernel and possibly crash the system.
 *
 * This function will check if the event has any fields flagged as needing
 * to be checked at runtime and perform those checks.
 *
 * If it is found that a field is unsafe, it will write into the @iter->seq
 * a message stating what was found to be unsafe.
 *
 * @return: true if the event is unsafe and should be ignored,
 *          false otherwise.
 */
void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
			 va_list ap)
bool ignore_event(struct trace_iterator *iter)
{
	const char *p = fmt;
	const char *str;
	int i, j;

	if (WARN_ON_ONCE(!fmt))
		return;
	struct ftrace_event_field *field;
	struct trace_event *trace_event;
	struct trace_event_call *event;
	struct list_head *head;
	struct trace_seq *seq;
	const void *ptr;

	if (static_branch_unlikely(&trace_no_verify))
		goto print;
	trace_event = ftrace_find_event(iter->ent->type);

	/* Don't bother checking when doing a ftrace_dump() */
	if (iter->fmt == static_fmt_buf)
		goto print;
	seq = &iter->seq;

	while (*p) {
		bool star = false;
		int len = 0;
	if (!trace_event) {
		trace_seq_printf(seq, "EVENT ID %d NOT FOUND?\n", iter->ent->type);
		return true;
	}

		j = 0;
	event = container_of(trace_event, struct trace_event_call, event);
	if (!(event->flags & TRACE_EVENT_FL_TEST_STR))
		return false;

		/* We only care about %s and variants */
		for (i = 0; p[i]; i++) {
			if (i + 1 >= iter->fmt_size) {
				/*
				 * If we can't expand the copy buffer,
				 * just print it.
				 */
				if (!trace_iter_expand_format(iter))
					goto print;
	head = trace_get_fields(event);
	if (!head) {
		trace_seq_printf(seq, "FIELDS FOR EVENT '%s' NOT FOUND?\n",
				 trace_event_name(event));
		return true;
	}

			if (p[i] == '\\' && p[i+1]) {
				i++;
				continue;
			}
			if (p[i] == '%') {
				/* Need to test cases like %08.*s */
				for (j = 1; p[i+j]; j++) {
					if (isdigit(p[i+j]) ||
					    p[i+j] == '.')
						continue;
					if (p[i+j] == '*') {
						star = true;
						continue;
					}
					break;
				}
				if (p[i+j] == 's')
					break;
				star = false;
			}
			j = 0;
		}
		/* If no %s found then just print normally */
		if (!p[i])
			break;
	/* Offsets are from the iter->ent that points to the raw event */
	ptr = iter->ent;

		/* Copy up to the %s, and print that */
		strncpy(iter->fmt, p, i);
		iter->fmt[i] = '\0';
		trace_seq_vprintf(&iter->seq, iter->fmt, ap);
	list_for_each_entry(field, head, link) {
		const char *str;
		bool good;

		/*
		 * If iter->seq is full, the above call no longer guarantees
		 * that ap is in sync with fmt processing, and further calls
		 * to va_arg() can return wrong positional arguments.
		 *
		 * Ensure that ap is no longer used in this case.
		 */
		if (iter->seq.full) {
			p = "";
			break;
		}
		if (!field->needs_test)
			continue;

		if (star)
			len = va_arg(ap, int);
		str = *(const char **)(ptr + field->offset);

		/* The ap now points to the string data of the %s */
		str = va_arg(ap, const char *);
		good = trace_safe_str(iter, str);

		/*
		 * If you hit this warning, it is likely that the
@@ -3956,45 +3878,14 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
		 * instead. See samples/trace_events/trace-events-sample.h
		 * for reference.
		 */
		if (WARN_ONCE(!trace_safe_str(iter, str, star, len),
			      "fmt: '%s' current_buffer: '%s'",
			      fmt, show_buffer(&iter->seq))) {
			int ret;

			/* Try to safely read the string */
			if (star) {
				if (len + 1 > iter->fmt_size)
					len = iter->fmt_size - 1;
				if (len < 0)
					len = 0;
				ret = copy_from_kernel_nofault(iter->fmt, str, len);
				iter->fmt[len] = 0;
				star = false;
			} else {
				ret = strncpy_from_kernel_nofault(iter->fmt, str,
								  iter->fmt_size);
		if (WARN_ONCE(!good, "event '%s' has unsafe pointer field '%s'",
			      trace_event_name(event), field->name)) {
			trace_seq_printf(seq, "EVENT %s: HAS UNSAFE POINTER FIELD '%s'\n",
					 trace_event_name(event), field->name);
			return true;
		}
			if (ret < 0)
				trace_seq_printf(&iter->seq, "(0x%px)", str);
			else
				trace_seq_printf(&iter->seq, "(0x%px:%s)",
						 str, iter->fmt);
			str = "[UNSAFE-MEMORY]";
			strcpy(iter->fmt, "%s");
		} else {
			strncpy(iter->fmt, p + i, j + 1);
			iter->fmt[j+1] = '\0';
	}
		if (star)
			trace_seq_printf(&iter->seq, iter->fmt, len, str);
		else
			trace_seq_printf(&iter->seq, iter->fmt, str);

		p += i + j + 1;
	}
 print:
	if (*p)
		trace_seq_vprintf(&iter->seq, p, ap);
	return false;
}

const char *trace_event_format(struct trace_iterator *iter, const char *fmt)
@@ -10542,8 +10433,6 @@ __init static int tracer_alloc_buffers(void)

	register_snapshot_cmd();

	test_can_verify();

	return 0;

out_free_pipe_cpumask:
+3 −3
Original line number Diff line number Diff line
@@ -648,9 +648,8 @@ void trace_buffer_unlock_commit_nostack(struct trace_buffer *buffer,

bool trace_is_tracepoint_string(const char *str);
const char *trace_event_format(struct trace_iterator *iter, const char *fmt);
void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
			 va_list ap) __printf(2, 0);
char *trace_iter_expand_format(struct trace_iterator *iter);
bool ignore_event(struct trace_iterator *iter);

int trace_empty(struct trace_iterator *iter);

@@ -1329,7 +1328,8 @@ struct ftrace_event_field {
	int			filter_type;
	int			offset;
	int			size;
	int			is_signed;
	unsigned int		is_signed:1;
	unsigned int		needs_test:1;
	int			len;
	KABI_RESERVE(1)
	KABI_RESERVE(2)
+22 −10
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ static int system_refcount_dec(struct event_subsystem *system)
	}

static struct ftrace_event_field *
__find_event_field(struct list_head *head, char *name)
__find_event_field(struct list_head *head, const char *name)
{
	struct ftrace_event_field *field;

@@ -114,7 +114,8 @@ trace_find_event_field(struct trace_event_call *call, char *name)

static int __trace_define_field(struct list_head *head, const char *type,
				const char *name, int offset, int size,
				int is_signed, int filter_type, int len)
				int is_signed, int filter_type, int len,
				int need_test)
{
	struct ftrace_event_field *field;

@@ -133,6 +134,7 @@ static int __trace_define_field(struct list_head *head, const char *type,
	field->offset = offset;
	field->size = size;
	field->is_signed = is_signed;
	field->needs_test = need_test;
	field->len = len;

	list_add(&field->link, head);
@@ -151,13 +153,13 @@ int trace_define_field(struct trace_event_call *call, const char *type,

	head = trace_get_fields(call);
	return __trace_define_field(head, type, name, offset, size,
				    is_signed, filter_type, 0);
				    is_signed, filter_type, 0, 0);
}
EXPORT_SYMBOL_GPL(trace_define_field);

static int trace_define_field_ext(struct trace_event_call *call, const char *type,
		       const char *name, int offset, int size, int is_signed,
		       int filter_type, int len)
		       int filter_type, int len, int need_test)
{
	struct list_head *head;

@@ -166,13 +168,13 @@ static int trace_define_field_ext(struct trace_event_call *call, const char *typ

	head = trace_get_fields(call);
	return __trace_define_field(head, type, name, offset, size,
				    is_signed, filter_type, len);
				    is_signed, filter_type, len, need_test);
}

#define __generic_field(type, item, filter_type)			\
	ret = __trace_define_field(&ftrace_generic_fields, #type,	\
				   #item, 0, 0, is_signed_type(type),	\
				   filter_type, 0);			\
				   filter_type, 0, 0);			\
	if (ret)							\
		return ret;

@@ -181,7 +183,8 @@ static int trace_define_field_ext(struct trace_event_call *call, const char *typ
				   "common_" #item,			\
				   offsetof(typeof(ent), item),		\
				   sizeof(ent.item),			\
				   is_signed_type(type), FILTER_OTHER, 0);	\
				   is_signed_type(type), FILTER_OTHER,	\
				   0, 0);				\
	if (ret)							\
		return ret;

@@ -332,6 +335,7 @@ static bool process_pointer(const char *fmt, int len, struct trace_event_call *c
/* Return true if the string is safe */
static bool process_string(const char *fmt, int len, struct trace_event_call *call)
{
	struct trace_event_fields *field;
	const char *r, *e, *s;

	e = fmt + len;
@@ -384,8 +388,16 @@ static bool process_string(const char *fmt, int len, struct trace_event_call *ca
	if (process_pointer(fmt, len, call))
		return true;

	/* Make sure the field is found, and consider it OK for now if it is */
	return find_event_field(fmt, call) != NULL;
	/* Make sure the field is found */
	field = find_event_field(fmt, call);
	if (!field)
		return false;

	/* Test this field's string before printing the event */
	call->flags |= TRACE_EVENT_FL_TEST_STR;
	field->needs_test = 1;

	return true;
}

/*
@@ -2564,7 +2576,7 @@ event_define_fields(struct trace_event_call *call)
			ret = trace_define_field_ext(call, field->type, field->name,
						 offset, field->size,
						 field->is_signed, field->filter_type,
						 field->len);
						 field->len, field->needs_test);
			if (WARN_ON_ONCE(ret)) {
				pr_err("error code is %d\n", ret);
				break;
+5 −1
Original line number Diff line number Diff line
@@ -317,10 +317,14 @@ EXPORT_SYMBOL(trace_raw_output_prep);

void trace_event_printf(struct trace_iterator *iter, const char *fmt, ...)
{
	struct trace_seq *s = &iter->seq;
	va_list ap;

	if (ignore_event(iter))
		return;

	va_start(ap, fmt);
	trace_check_vprintf(iter, trace_event_format(iter, fmt), ap);
	trace_seq_vprintf(s, trace_event_format(iter, fmt), ap);
	va_end(ap);
}
EXPORT_SYMBOL(trace_event_printf);