diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-07-12 09:46:37 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-07-12 09:46:37 -0700 |
| commit | 6205562c5904ee23786239298299043876b1a977 (patch) | |
| tree | 64ebf8fe15902607dd06155c9a1f81d498bc3748 /kernel | |
| parent | 44696aa3a489d2baf58efa61b37833f100072bee (diff) | |
| parent | 601ddaceb861be7eb557278109966320a6f3478c (diff) | |
Merge tag 'trace-v7.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull tracing fixes from Steven Rostedt:
- Free field in error path of synthetic event parse
In __create_synth_event() the field was allocated but was not freed
in the error path
- Fix ring_buffer_event_length() on 8 byte aligned architectures
On architectures with CONFIG_HAVE_64BIT_ALIGNED_ACCESS set to y, the
ring_buffer_event_length() may return the wrong size. This is because
archs with that config set will always use the "big event meta
header" as that is 8 bytes keeping the payload 8 bytes aligned, even
when a 4 byte header could hold the size of the event
But ring_buffer_event_length() doesn't take this into account and
only subtracts 4 bytes for the meta header in the length when it
should have subtracted 8 bytes
- Have osnoise wait for a full rcu synchronization on unregister
osnoise_unregister_instance() used to call synchronize_rcu() before
freeing its copy of the instance but was switched to kfree_rcu(). The
osniose tracer has code that traverses the instances that it uses,
and inst is just a pointer to that instance. By using kfree_rcu()
instead of synchronize_rcu(), the instance that the inst pointer is
pointing to can be freed while the osnoise code is still referencing
it
That is, a rmdir on an instance first unregisters the tracer. When
the unregister finishes, the rmdir expects that the tracer is
finished with the instance that it is using. By putting back the
synchronize_rcu() in osnoise_unregister_instance() the unregistering
of osnoise will now return when all the users of the instance have
finished
- Remove an unused setting of "ret" in tracing_set_tracer()
- Fix ring_buffer_read_page() copying events
The commit that changed ring_buffer_read_page() to show dropped
events from the buffer itself, split the "commit" variable between
the commit value (with flags) and "size" that holds the size of the
sub-buffer. A cut and paste error changed the test of the reading
from checking the size of the buffer to the size of the event causing
reads to only read one event at a time
- Make tracepoint_printk a static variable
When the tracing sysctl knobs were move from sysctl.c to trace.c, the
variable tracepoint_printk no longer needed to be global. Make it
static
- Fix some typos
- Fix NULL pointer dereference in func_set_flag()
The flags update of the function tracer first checks if the value of
the flag is the same and exits if they are, and then it checks if the
current tracer is the function tracer and exits if it isn't. The
problem is that these checks need to be in a reversed order, as if
the tracer isn't the function tracer, then the flag being checked may
not exist. Reverse the order of these checks
- Fix ufs core trace events to not dereference a pointer in TP_printk()
The TP_printk() part of the TRACE_EVENT() macro is called when the
user reads the "trace" file. This can be seconds, minutes, hours,
days, weeks, and even months after the data was recorded into the
ring buffer. Thus, saving a pointer to an object into the ring buffer
and then dereferencing it from TP_printk() can cause harm as the
object the pointer is pointing to may no longer exist
Fix all the trace events in ufs core to save the device name in the
ring buffer instead of dereferencing the device descriptor from
TP_printk()
- Prevent out-of-bound reads in glob matching of trace events
The filter logic of events allows simple glob logic to add wild cards
to filter on strings. But some events have fields that may not have a
terminating 'nul' character. This may cause the glob matching to go
beyond the string. Change the logic to always pass in the length of
the field that is being matched
- Add no-rcu-check version of trace_##event##_enabled()
The trace_##event##_enabled() usually wraps trace events to do extra
work that is only needed when the trace event is enabled. But this
can hide events that are placed in locations where RCU is not
watching, and can make lockdep not see these bugs when the event is
not enabled
The trace_##event##_enabled() was updated to always test to make sure
RCU is watching to catch locations that may call events without RCU
being active
This caused a false positive for the irq_disabled() and related
events. As that use trace_irq_disabled_enabled() to force RCU to be
watching when the event is enabled via the ct_irq_enter() function,
calls the event, and then calls ct_irq_exit() to put RCU back to its
original state
The trace_irq_disabled_enabled() should not trigger a warning when
RCU is not watching because the code within its block handles the
case properly. Make a __trace_##event##_enabled() version for this
event to use that doesn't check RCU is watching as it handles the
case when it isn't
- Fix use-after-free in user_event_mm_dup()
When the enabler is removed from the link list, it is freed
immediately. But it is protected via RCU and needs to be freed after
an RCU grace period. Use queue_rcu_work() so that the event_mutex can
also be taken as user_event_put() takes the mutex on the last
reference is released
- Free type string in error path of parse_synth_field()
There's an error path in parse_synth_field() where the allocated type
string is not freed
- Add selftest that tests deferred event teardown
- Fix leak in error path of trace_remote_alloc_buffer()
If page allocation fails, the desc->nr_cpus is not incremented for
the current CPU and the allocations done for it are not freed
- Fix allocation length in trace_remote_alloc_buffer()
The logic to calculate the struct_len was doing a double count and
setting the value too large. Calculate the size upfront to fix the
error and simplify the logic
- Fix sparse CPU masks in ring_buffer_desc()
If there are sparse CPUs (gaps in the numbering), the
ring_buffer_desc() will fail as it tests the CPU number against the
number of CPUs that are used
* tag 'trace-v7.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
ring-buffer: Allow sparse CPU masks in ring_buffer_desc()
tracing/remotes: Fix struct_len in trace_remote_alloc_buffer()
tracing/remotes: Fix leak in trace_remote_alloc_buffer() error path
selftests/user_events: Wait for deferred event teardown after unregister
tracing/synthetic: Free type string on error path
tracing/user_events: Fix use-after-free in user_event_mm_dup()
tracing: Add a no-rcu-check version of trace_##event##_enabled()
tracing: Prevent out-of-bounds read in glob matching
ufs: core: tracing: Do not dereference pointers in TP_printk()
tracing: Fix NULL pointer dereference in func_set_flag()
samples: ftrace: Fix typos in benchmark comment
tracing: Make tracepoint_printk static as not exported
ring-buffer: Fix ring_buffer_read_page() copying only one event per page
tracing: Remove unused ret assignment in tracing_set_tracer()
tracing/osnoise: Call synchronize_rcu() when unregistering
ring-buffer: Fix event length with forced 8-byte alignment
tracing/synthetic: Free pending field on error path
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/trace/ring_buffer.c | 10 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 3 | ||||
| -rw-r--r-- | kernel/trace/trace_events_filter.c | 6 | ||||
| -rw-r--r-- | kernel/trace/trace_events_synth.c | 10 | ||||
| -rw-r--r-- | kernel/trace/trace_events_user.c | 39 | ||||
| -rw-r--r-- | kernel/trace/trace_functions.c | 8 | ||||
| -rw-r--r-- | kernel/trace/trace_osnoise.c | 4 | ||||
| -rw-r--r-- | kernel/trace/trace_preemptirq.c | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_remote.c | 18 |
9 files changed, 60 insertions, 40 deletions
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 56a328e94395..804ccae694d2 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -270,7 +270,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event) if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX) return length; length -= RB_EVNT_HDR_SIZE; - if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0])) + if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) || + RB_FORCE_8BYTE_ALIGNMENT) length -= sizeof(event->array[0]); return length; } @@ -2329,10 +2330,7 @@ static struct ring_buffer_desc *ring_buffer_desc(struct trace_buffer_desc *trace size_t len; int i; - if (!trace_desc) - return NULL; - - if (cpu >= trace_desc->nr_cpus) + if (!trace_desc || !trace_desc->nr_cpus) return NULL; end = (struct ring_buffer_desc *)((void *)trace_desc + trace_desc->struct_len); @@ -7174,7 +7172,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, rpos = reader->read; pos += event_size; - if (rpos >= event_size) + if (rpos >= size) break; event = rb_reader_event(cpu_buffer); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 1146b83b711a..18710c190c92 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -87,7 +87,7 @@ void __init disable_tracing_selftest(const char *reason) /* Pipe tracepoints to printk */ static struct trace_iterator *tracepoint_print_iter; -int tracepoint_printk; +static int tracepoint_printk; static bool tracepoint_printk_stop_on_boot __initdata; static bool traceoff_after_boot __initdata; static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key); @@ -5015,7 +5015,6 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf) RING_BUFFER_ALL_CPUS); if (ret < 0) return ret; - ret = 0; } list_for_each_entry(t, &tr->tracers, list) { diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 609325f57942..6385cd662d8d 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len) return 0; } -static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused) +static int regex_match_glob(char *str, struct regex *r, int len) { - if (glob_match(r->pattern, str)) - return 1; - return 0; + return glob_match_len(r->pattern, str, len) ? 1 : 0; } /** diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index e6871230bde9..dc15658a887c 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -839,8 +839,10 @@ static struct synth_field *parse_synth_field(int argc, char **argv, seq_buf_puts(&s, "__data_loc "); seq_buf_puts(&s, field->type); - if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) + if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) { + kfree(type); goto free; + } s.buffer[s.len] = '\0'; kfree(field->type); @@ -1446,13 +1448,13 @@ static int __create_synth_event(const char *name, const char *raw_fields) if (cmd_version > 1 && n_fields_this_loop >= 1) { synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str)); ret = -EINVAL; - goto err_free_arg; + goto err_free_field; } if (n_fields == SYNTH_FIELDS_MAX) { synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0); ret = -EINVAL; - goto err_free_arg; + goto err_free_field; } fields[n_fields++] = field; @@ -1491,6 +1493,8 @@ static int __create_synth_event(const char *name, const char *raw_fields) kfree(saved_fields); return ret; + err_free_field: + free_synth_field(field); err_free_arg: argv_free(argv); err: diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index c4ba484f7b38..8c82ecb735f4 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -109,6 +109,9 @@ struct user_event_enabler { /* Track enable bit, flags, etc. Aligned for bitops. */ unsigned long values; + + /* Defer the event put and enabler free past an RCU grace period. */ + struct rcu_work put_rwork; }; /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */ @@ -396,17 +399,39 @@ error: return NULL; }; -static void user_event_enabler_destroy(struct user_event_enabler *enabler, - bool locked) +static void delayed_user_event_enabler_put(struct work_struct *work) { - list_del_rcu(&enabler->mm_enablers_link); + struct user_event_enabler *enabler = container_of(to_rcu_work(work), + struct user_event_enabler, put_rwork); /* No longer tracking the event via the enabler */ - user_event_put(enabler->event, locked); + user_event_put(enabler->event, false); + /* Run from queue_rcu_work(), the RCU grace period has elapsed */ kfree(enabler); } +static void user_event_enabler_destroy(struct user_event_enabler *enabler) +{ + list_del_rcu(&enabler->mm_enablers_link); + + /* + * The enabler is removed from an RCU-traversed list + * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only), + * and readers there dereference enabler->event and take a new ref on + * it. Both the put of that event reference and the free of the enabler + * therefore have to wait for a grace period so no reader can be looking + * at the enabler or racing the last put of its event. + * + * The put itself must not run in RCU context: when it drops the last + * reference user_event_put() takes event_mutex, which cannot be taken + * from a softirq/RCU callback. Defer both to a work item scheduled + * after a grace period via queue_rcu_work(). + */ + INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put); + queue_rcu_work(system_percpu_wq, &enabler->put_rwork); +} + static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr, int attempt) { @@ -464,7 +489,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work) /* User asked for enabler to be removed during fault */ if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) { - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); goto out; } @@ -764,7 +789,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm) struct user_event_enabler *enabler, *next; list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link) - user_event_enabler_destroy(enabler, false); + user_event_enabler_destroy(enabler); mmdrop(mm->mm); kfree(mm); @@ -2645,7 +2670,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) flags |= enabler->values & ENABLE_VAL_COMPAT_MASK; if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler))) - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); /* Removed at least one */ ret = 0; diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index f283391a4dc8..cd37f2013758 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -458,12 +458,12 @@ func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) ftrace_func_t func; u32 new_flags; - /* Do nothing if already set. */ - if (!!set == !!(tr->current_trace_flags->val & bit)) + /* We can change this flag only when current tracer is function. */ + if (tr->current_trace != &function_trace) return 0; - /* We can change this flag only when not running. */ - if (tr->current_trace != &function_trace) + /* Do nothing if already set. */ + if (!!set == !!(tr->current_trace_flags->val & bit)) return 0; new_flags = (tr->current_trace_flags->val & ~bit) | (set ? bit : 0); diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c index 5e83c4f6f2b4..0e1265acd1cc 100644 --- a/kernel/trace/trace_osnoise.c +++ b/kernel/trace/trace_osnoise.c @@ -179,7 +179,9 @@ static void osnoise_unregister_instance(struct trace_array *tr) if (!found) return; - kvfree_rcu_mightsleep(inst); + /* Do a full sync to ensure that tr remains valid, not just inst */ + synchronize_rcu(); + kvfree(inst); } /* diff --git a/kernel/trace/trace_preemptirq.c b/kernel/trace/trace_preemptirq.c index 0c42b15c3800..b63e3558948f 100644 --- a/kernel/trace/trace_preemptirq.c +++ b/kernel/trace/trace_preemptirq.c @@ -30,7 +30,7 @@ #else #define trace(point, args) \ do { \ - if (trace_##point##_enabled()) { \ + if (__trace_##point##_enabled()) { \ bool exit_rcu = false; \ if (in_nmi()) \ break; \ diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index 2a6cc000ec98..0f6ef5c36d84 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -979,33 +979,30 @@ EXPORT_SYMBOL_GPL(trace_remote_free_buffer); int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, size_t buffer_size, const struct cpumask *cpumask) { + size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask)); unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1; - void *desc_end = desc + desc_size; struct ring_buffer_desc *rb_desc; int cpu, ret = -ENOMEM; - if (desc_size < struct_size(desc, __data, 0)) + if (desc_size < min_desc_size) return -EINVAL; desc->nr_cpus = 0; - desc->struct_len = struct_size(desc, __data, 0); + desc->struct_len = min_desc_size; - rb_desc = (struct ring_buffer_desc *)&desc->__data[0]; + rb_desc = __first_ring_buffer_desc(desc); for_each_cpu(cpu, cpumask) { unsigned int id; - if ((void *)rb_desc + struct_size(rb_desc, page_va, nr_pages) > desc_end) { - ret = -EINVAL; - goto err; - } - rb_desc->cpu = cpu; rb_desc->nr_page_va = 0; rb_desc->meta_va = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->meta_va) goto err; + desc->nr_cpus++; + for (id = 0; id < nr_pages; id++) { rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->page_va[id]) @@ -1013,9 +1010,6 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, rb_desc->nr_page_va++; } - desc->nr_cpus++; - desc->struct_len += offsetof(struct ring_buffer_desc, page_va); - desc->struct_len += struct_size(rb_desc, page_va, rb_desc->nr_page_va); rb_desc = __next_ring_buffer_desc(rb_desc); } |
