Currently the reader of set_ftrace_filter and set_ftrace_notrace just adds the pointer to the global tracer hash to its iterator. Unlike the writer that allocates a copy of the hash, the reader keeps the pointer to the filter hashes. This is problematic because this pointer is static across function calls that release the locks that can update the global tracer hashes. This can cause UAF and similar bugs. Allocate and copy the hash for reading the filter files like it is done for the writers. This not only fixes UAF bugs, but also makes the code a bit simpler as it doesn't have to differentiate when to free the iterator's hash between writers and readers.
Unified · Split
| @@ -4661,13 +4661,17 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, · 4660 unmodified lines | ||
| 4661 | 4661 | } else { |
| 4662 | 4662 | iter->hash = alloc_and_copy_ftrace_hash(size_bits, hash); |
| 4663 | 4663 | } |
| 4664 | + } else { | |
| 4665 | + if (hash) | |
| 4666 | + iter->hash = alloc_and_copy_ftrace_hash(hash->size_bits, hash); | |
| 4667 | + else | |
| 4668 | + iter->hash = EMPTY_HASH; | |
| 4669 | + } | |
| 4664 | 4670 | |
| 4665 | − if (!iter->hash) { | |
| 4666 | − trace_parser_put(&iter->parser); | |
| 4667 | − goto out_unlock; | |
| 4668 | − } | |
| 4669 | − } else | |
| 4670 | − iter->hash = hash; | |
| 4671 | + if (!iter->hash) { | |
| 4672 | + trace_parser_put(&iter->parser); | |
| 4673 | + goto out_unlock; | |
| 4674 | + } | |
| 4671 | 4675 | |
| 4672 | 4676 | ret = 0; |
| 4673 | 4677 | |
| @@ -6543,9 +6547,6 @@ int ftrace_regex_release(struct inode *inode, struct file *file) · 1869 unmodified lines | ||
| 6543 | 6547 | ftrace_hash_move_and_update_ops(iter->ops, orig_hash, |
| 6544 | 6548 | iter->hash, filter_hash); |
| 6545 | 6549 | mutex_unlock(&ftrace_lock); |
| 6546 | − } else { | |
| 6547 | − /* For read only, the hash is the ops hash */ | |
| 6548 | − iter->hash = NULL; | |
| 6549 | 6550 | } |
| 6550 | 6551 | |
| 6551 | 6552 | mutex_unlock(&iter->ops->func_hash->regex_lock); |