user_event_validate() checks that a dynamic field lies inside the event but
does not verify the minimum size required by its filter type.  A one-byte
dynamic cpumask can therefore reach filter_pred_cpumask(), which consumes
cpumask_size() bytes beyond the event buffer.

KASAN reports:

  BUG: KASAN: slab-use-after-free in filter_match_preds
  Read of size 8
  filter_match_preds
  trace_event_buffer_commit
  user_event_ftrace
  user_events_write_core

Mark dynamic cpumask fields and reject locations shorter than
cpumask_size().

The patched kernel rejects the short event and accepts a full-size control
event without a KASAN report.

Fixes: 2467cda1b5c9 ("user_events: Validate user payloads for size and null 
termination")
Signed-off-by: Yifan Zhang <[email protected]>
---
 kernel/trace/trace_events_user.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index f658c3a77aa7a..85938b45b7618 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -170,6 +170,7 @@ struct user_event_file_info {
 
 #define VALIDATOR_ENSURE_NULL (1 << 0)
 #define VALIDATOR_REL (1 << 1)
+#define VALIDATOR_CPUMASK BIT(2)
 
 struct user_event_validator {
        struct list_head        user_event_link;
@@ -1159,6 +1160,9 @@ static int user_event_add_field(struct user_event *user, 
const char *type,
        if (strstr(type, "char") != NULL)
                validator_flags |= VALIDATOR_ENSURE_NULL;
 
+       if (strstr(type, "cpumask_t"))
+               validator_flags |= VALIDATOR_CPUMASK;
+
        validator = kmalloc_obj(*validator, GFP_KERNEL_ACCOUNT);
 
        if (!validator) {
@@ -1598,6 +1602,10 @@ static int user_event_validate(struct user_event *user, 
void *data, int len)
                offset = loc & 0xffff;
                size = loc >> 16;
 
+               if (unlikely((validator->flags & VALIDATOR_CPUMASK) &&
+                            size < cpumask_size()))
+                       return -EFAULT;
+
                if (likely(validator->flags & VALIDATOR_REL))
                        pos += offset + sizeof(loc);
                else
-- 
2.39.5 (Apple Git-154)

Reply via email to