tracing: Fixes for v6.15
Subsystems (4)
Steven Rostedt
maintainercover tracing: Fixes for v6.15
tracing fixes for v6.15
- Initialize hash variables in ftrace subops logic
The fix that simplified the ftrace subops logic opened a path where some variables could be used without being initialized, and done subtly where the compiler did not catch it. Initialize those variables to the EMPTY_HASH, which is the default hash.
- Reinitialize the hash pointers after they are freed
Some of the hash pointers in the subop logic were freed but may still be referenced later. To prevent use-after-free bugs, initialize them back to the EMPTY_HASH.
- Free the ftrace hashes when they are replaced
The fix that simplified the subops logic updated some hash pointers, but left the original hash that they were pointing to where they are no longer used. This caused a memory leak. Free the hashes that are pointed to by the pointers when they are replaced.
- Fix size initialization of ftrace direct function hash
The ftrace direct function hash used by BPF initialized the hash size incorrectly. It checked the size of items to a hard coded 32, which made the hash bit size of 5. The hash size is supposed to be limited by the bit size of the hash, as the bitmask is allowed to be greater than 5. Rework the size check to first pass the number of elements to fls() and then compare that to FTRACE_HASH_MAX_BITS before allocating the hash.
- Fix format output of ftrace_graph_ent_entry event
The field depth of the ftrace_graph_ent_entry event is of size 4 but the output showed it as unsigned long and use "%lu". Change it to unsigned int and use "%u" in the print format that is displayed to user space.
- Fix the trace event filter on strings
Events can be filtered on numbers or string values. The return value checked from strncpy_from_kernel_nofault() and strncpy_from_user_nofault() was used to determine if reading the strings would fault or not. It would return fault if the value was non zero, which is basically meant that it was always considering the read as a fault.
- Add selftest to test trace event string filtering
In order to catch the breakage of the string filtering, add a self test to make sure that it continues to work.
git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
trace/fixes
Head SHA1: d481ee35247d2a01764667a25f6f512c292ba42d
Ilya Leoshkevich (1):
ftrace: Fix type of ftrace_graph_ent_entry.depth
Menglong Dong (1):
ftrace: fix incorrect hash size in register_ftrace_direct()
Steven Rostedt (5):
ftrace: Initialize variables for ftrace_startup/shutdown_subops()
ftrace: Reinitialize hash to EMPTY_HASH after freeing
ftrace: Free ftrace hashes after they are replaced in the subops code
tracing: Fix filter string testing
tracing: selftests: Add testing a user string to filters
----
kernel/trace/ftrace.c | 27 +++++++++++++++------- kernel/trace/trace_entries.h | 4 ++-- kernel/trace/trace_events_filter.c | 4 ++-- .../ftrace/test.d/filter/event-filter-function.tc | 20 ++++++++++++++++ 4 files changed, 43 insertions(+), 12 deletions(-)
Steven Rostedt
maintainerPATCH 1/7 ftrace: Initialize variables for ftrace_startup/shutdown_subops()
From: Steven Rostedt <[email protected]>
The reworking to fix and simplify the ftrace_startup_subops() and the ftrace_shutdown_subops() made it possible for the filter_hash and notrace_hash variables to be used uninitialized in a way that the compiler did not catch it.
Initialize both filter_hash and notrace_hash to the EMPTY_HASH as that is what they should be if they never are used.
--- kernel/trace/ftrace.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
kernel/trace/ftrace.c +4 -4
@@ -3490,8 +3490,8 @@ static int add_next_hash(struct ftrace_hash **filter_hash, struct ftrace_hash ***/ int ftrace_startup_subops (struct ftrace_ops *ops,struct ftrace_ops *subops,int command) { -struct ftrace_hash *filter_hash; -struct ftrace_hash *notrace_hash; +struct ftrace_hash *filter_hash =EMPTY_HASH ; +struct ftrace_hash *notrace_hash =EMPTY_HASH ;struct ftrace_hash *save_filter_hash;struct ftrace_hash *save_notrace_hash;int ret;
@@ -3625,8 +3625,8 @@ static int rebuild_hashes(struct ftrace_hash **filter_hash, struct ftrace_hash **/ int ftrace_shutdown_subops (struct ftrace_ops *ops,struct ftrace_ops *subops,int command) { -struct ftrace_hash *filter_hash; -struct ftrace_hash *notrace_hash; +struct ftrace_hash *filter_hash =EMPTY_HASH ; +struct ftrace_hash *notrace_hash =EMPTY_HASH ;int ret;if (unlikely (ftrace_disabled))
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 2/7 ftrace: Reinitialize hash to EMPTY_HASH after freeing
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
There's several locations that free a ftrace hash pointer but may be referenced again. Reset them to EMPTY_HASH so that a u-a-f bug doesn't happen.
--- kernel/trace/ftrace.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/trace/ftrace.c +4 -0
@@ -1297,6 +1297,8 @@ void ftrace_free_filter(struct ftrace_ops *ops)return ;free_ftrace_hash (ops->func_hash->filter_hash);free_ftrace_hash (ops->func_hash->notrace_hash); + ops->func_hash->filter_hash =EMPTY_HASH ; + ops->func_hash->notrace_hash =EMPTY_HASH ; }EXPORT_SYMBOL_GPL (ftrace_free_filter);
@@ -3443,6 +3445,7 @@ static int add_next_hash(struct ftrace_hash **filter_hash, struct ftrace_hash ** size_bits);if (ret <0 ) {free_ftrace_hash (*filter_hash); + *filter_hash =EMPTY_HASH ;return ret; } }
@@ -3472,6 +3475,7 @@ static int add_next_hash(struct ftrace_hash **filter_hash, struct ftrace_hash ** subops_hash->notrace_hash);if (ret <0 ) {free_ftrace_hash (*notrace_hash); + *notrace_hash =EMPTY_HASH ;return ret; } }
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 3/7 ftrace: Free ftrace hashes after they are replaced in the subops code
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The subops processing creates new hashes when adding and removing subops. There were some places that the old hashes that were replaced were not freed and this caused some memory leaks.
--- kernel/trace/ftrace.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
kernel/trace/ftrace.c +7 -1
@@ -3609,6 +3609,9 @@ static int rebuild_hashes(struct ftrace_hash **filter_hash, struct ftrace_hash * } } +free_ftrace_hash (temp_hash.filter_hash); +free_ftrace_hash (temp_hash.notrace_hash); + temp_hash.filter_hash = *filter_hash; temp_hash.notrace_hash = *notrace_hash; }
@@ -3703,8 +3706,11 @@ static int ftrace_hash_move_and_update_subops(struct ftrace_ops *subops, } ret =rebuild_hashes (&filter_hash, ¬race_hash, ops); -if (!ret) +if (!ret) { ret =ftrace_update_ops (ops, filter_hash, notrace_hash); +free_ftrace_hash (filter_hash); +free_ftrace_hash (notrace_hash); + }if (ret) {/* Put back the original hash */
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 4/7 ftrace: fix incorrect hash size in register_ftrace_direct()
In reply to Steven RostedtFrom: Menglong Dong <[email protected]>
The maximum of the ftrace hash bits is made fls(32) in register_ftrace_direct(), which seems illogical. So, we fix it by making the max hash bits FTRACE_HASH_MAX_BITS instead.
--- kernel/trace/ftrace.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
kernel/trace/ftrace.c +4 -3
@@ -5964,9 +5964,10 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)/* Make a copy hash to place the new and the old entries in */ size = hash->count + direct_functions->count; -if (size >32 ) - size =32 ; - new_hash =alloc_ftrace_hash (fls (size)); + size =fls (size); +if (size >FTRACE_HASH_MAX_BITS ) + size =FTRACE_HASH_MAX_BITS ; + new_hash =alloc_ftrace_hash (size);if (!new_hash) goto out_unlock;
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 5/7 ftrace: Fix type of ftrace_graph_ent_entry.depth
In reply to Steven RostedtFrom: Ilya Leoshkevich <[email protected]>
ftrace_graph_ent.depth is int, but ftrace_graph_ent_entry.depth is unsigned long. This confuses trace-cmd on 64-bit big-endian systems and makes it print a huge amount of spaces. Fix this by using unsigned int, which has a matching size, instead.
--- kernel/trace/trace_entries.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/trace/trace_entries.h +2 -2
@@ -80,11 +80,11 @@ FTRACE_ENTRY(funcgraph_entry, ftrace_graph_ent_entry, F_STRUCT( __field_struct( struct ftrace_graph_ent, graph_ent ) __field_packed( unsigned long, graph_ent, func ) - __field_packed( unsigned long, graph_ent, depth ) + __field_packed( unsigned int, graph_ent, depth ) __dynamic_array(unsigned long, args ) ), - F_printk("--> %ps (%lu)", (void *)__entry->func, __entry->depth) + F_printk("--> %ps (%u)", (void *)__entry->func, __entry->depth) );#ifdef CONFIG_FUNCTION_GRAPH_RETADDR
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 6/7 tracing: Fix filter string testing
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The filter string testing uses strncpy_from_kernel/user_nofault() to retrieve the string to test the filter against. The if() statement was incorrect as it considered 0 as a fault, when it is only negative that it faulted.
Running the following commands:
# cd /sys/kernel/tracing # echo "filename.ustring ~ \"/proc*\"" > events/syscalls/sys_enter_openat/filter # echo 1 > events/syscalls/sys_enter_openat/enable # ls /proc/$$/maps # cat trace
Would produce nothing, but with the fix it will produce something like:
ls-1192 [007] ..... 8169.828333: sys_openat(dfd: ffffffffffffff9c, filename: 7efc18359904, flags: 80000, mode: 0)
--- kernel/trace/trace_events_filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/trace/trace_events_filter.c +2 -2
@@ -808,7 +808,7 @@ static __always_inline char *test_string(char *str) kstr = ubuf->buffer;/* For safety, do not trust the string pointer */ -if (!strncpy_from_kernel_nofault (kstr, str,USTRING_BUF_SIZE )) +if (strncpy_from_kernel_nofault (kstr, str,USTRING_BUF_SIZE ) <0 )return NULL ;return kstr; }
@@ -827,7 +827,7 @@ static __always_inline char *test_ustring(char *str)/* user space address? */ ustr = (char __user *)str; -if (!strncpy_from_user_nofault (kstr, ustr,USTRING_BUF_SIZE )) +if (strncpy_from_user_nofault (kstr, ustr,USTRING_BUF_SIZE ) <0 )return NULL ;return kstr;
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 7/7 tracing: selftests: Add testing a user string to filters
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Running the following commands was broken:
# cd /sys/kernel/tracing # echo "filename.ustring ~ \"/proc*\"" > events/syscalls/sys_enter_openat/filter # echo 1 > events/syscalls/sys_enter_openat/enable # ls /proc/$$/maps # cat trace
And would produce nothing when it should have produced something like:
ls-1192 [007] ..... 8169.828333: sys_openat(dfd: ffffffffffffff9c, filename: 7efc18359904, flags: 80000, mode: 0)
Add a test to check this case so that it will be caught if it breaks again.
--- .../test.d/filter/event-filter-function.tc | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+)
tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc +20 -0
@@ -80,6 +80,26 @@ if [ $misscnt -gt 0 ]; then exit_fail fi +# Check strings too +if [ -f events/syscalls/sys_enter_openat/filter ]; then + DIRNAME=`basename $TMPDIR` + echo "filename.ustring ~ \"*$DIRNAME*\"" > events/syscalls/sys_enter_openat/filter + echo 1 > events/syscalls/sys_enter_openat/enable + echo 1 > tracing_on + ls /bin/sh + nocnt=`grep openat trace | wc -l` + ls $TMPDIR + echo 0 > tracing_on + hitcnt=`grep openat trace | wc -l`; + echo 0 > events/syscalls/sys_enter_openat/enable + if [ $nocnt -gt 0 ]; then + exit_fail + fi + if [ $hitcnt -eq 0 ]; then + exit_fail + fi +fi + reset_events_filter exit 0
Signature · 1 line
2.47.2