Memory safety (null pointer dereference) and improper cleanup on allocation failure in tracing code

MEDIUM
torvalds/linux
Commit: 23884007afe9
Affected: Linux kernel v7.0-rc6 and earlier in the tracing subsystem; fixed in trace-v7.1-rc4.
2026-05-25 23:49 UTC

Description

Two memory-safety issues were addressed in this commit: 1) hist_field_name(): Previously, when the formatted histogram field name overflowed the local buffer, the function returned NULL, which could be passed to strcat and cause a crash or memory corruption. The fix changes the behavior to return a zero-length string (empty string) in truncation cases, avoiding NULL pointers being passed to string-ops and reducing crash risk. 2) tracing_map_elt_free on allocation failure: When elt_alloc() fails, the code previously attempted to call map->ops->elt_free(), which may not be safe since the allocation failed and the object isn’t fully initialized. The patch ensures elt_free is only invoked on successfully allocated elements by introducing a private __tracing_map_elt_free() helper and calling it in the failure path, while the public tracing_map_elt_free() wrapper only calls elt_free for fully initialized elements. Overall, these changes improve memory-safety in the tracing subsystem by avoiding NULL-dereference scenarios and unsafe cleanup paths during allocation failures.

Commit Details

Author: Linus Torvalds

Date: 2026-05-22 13:09 UTC

Message:

Merge tag 'trace-v7.1-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing fixes from Steven Rostedt: - Avoid NULL return from hist_field_name() The function hist_field_name() is directly passed to a strcat() which does not handle "NULL" characters. Return a zero length string when size is greater than the limit. This is used only to output already created histograms and no field currently is greater than the limit. But it should still not return NULL. - Do not call map->ops->elt_free() on allocation failure When elt_alloc() fails, it should not call the map->ops->elt_free() function if it exists, as that function may not be able to handle the free on allocation failures. The ->elt_free() should only be called when elt_alloc() succeeds. * tag 'trace-v7.1-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: tracing: Do not call map->ops->elt_free() if elt_alloc() fails tracing: Avoid NULL return from hist_field_name() on truncation

Triage Assessment

Vulnerability Type: Memory safety

Confidence: MEDIUM

Reasoning:

The changes address memory safety issues that could lead to crashes or undefined behavior in tracing code: avoiding returning NULL to strcat (which could crash or be exploited when formatting strings) and ensuring elt_free is only called on successful allocations. While not a direct, well-known vulnerability like RCE/SQLi, these improvements reduce crashy behavior that could be exploited in certain contexts and are security-relevant as memory safety fixes.

Verification Assessment

Vulnerability Type: Memory safety (null pointer dereference) and improper cleanup on allocation failure in tracing code

Confidence: MEDIUM

Affected Versions: Linux kernel v7.0-rc6 and earlier in the tracing subsystem; fixed in trace-v7.1-rc4.

Code Diff

diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 0dbbf6cca9bc94..eb2c2bc8bc3d52 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -1369,10 +1369,8 @@ static const char *hist_field_name(struct hist_field *field, len = snprintf(full_name, sizeof(full_name), fmt, field->system, field->event_name, field->name); - if (len >= sizeof(full_name)) - return NULL; - - field_name = full_name; + if (len < sizeof(full_name)) + field_name = full_name; } else field_name = field->name; } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c index bf1a507695b6d6..0dd7927df22ac4 100644 --- a/kernel/trace/tracing_map.c +++ b/kernel/trace/tracing_map.c @@ -386,13 +386,11 @@ static void tracing_map_elt_init_fields(struct tracing_map_elt *elt) } } -static void tracing_map_elt_free(struct tracing_map_elt *elt) +static void __tracing_map_elt_free(struct tracing_map_elt *elt) { if (!elt) return; - if (elt->map->ops && elt->map->ops->elt_free) - elt->map->ops->elt_free(elt); kfree(elt->fields); kfree(elt->vars); kfree(elt->var_set); @@ -400,6 +398,17 @@ static void tracing_map_elt_free(struct tracing_map_elt *elt) kfree(elt); } +static void tracing_map_elt_free(struct tracing_map_elt *elt) +{ + if (!elt) + return; + + /* Only objects initialized with alloc_elt() should be passed to free_elt().*/ + if (elt->map->ops && elt->map->ops->elt_free) + elt->map->ops->elt_free(elt); + __tracing_map_elt_free(elt); +} + static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map) { struct tracing_map_elt *elt; @@ -444,7 +453,7 @@ static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map) } return elt; free: - tracing_map_elt_free(elt); + __tracing_map_elt_free(elt); return ERR_PTR(err); }
← Back to Alerts View on GitHub →