]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
user_events: Add trace event call as root for low permission cases
authorBeau Belgrave <beaub@linux.microsoft.com>
Tue, 8 Mar 2022 22:28:07 +0000 (14:28 -0800)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Tue, 15 Mar 2022 18:33:20 +0000 (14:33 -0400)
Tracefs by default is locked down heavily. System operators can open up
some files, such as user_events to a broader set of users. These users
do not have access within tracefs beyond just the user_event files. Due
to this restriction the trace_add_event_call/remove calls will silently
fail since the caller does not have permissions to create directories.

To fix this trace_add_event_call/remove calls will be issued with
override creds of the global root UID. Creds are reverted immediately
afterward.

Link: https://lkml.kernel.org/r/20220308222807.2040-1-beaub@linux.microsoft.com
Signed-off-by: Beau Belgrave <beaub@linux.microsoft.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
kernel/trace/trace_events_user.c

index e10ad057e797abc53d8befd3141b762582c86108..8b3d241a31c2606e99f76a33a94fed8092e40971 100644 (file)
@@ -562,6 +562,41 @@ static struct trace_event_functions user_event_funcs = {
        .trace = user_event_print_trace,
 };
 
+static int user_event_set_call_visible(struct user_event *user, bool visible)
+{
+       int ret;
+       const struct cred *old_cred;
+       struct cred *cred;
+
+       cred = prepare_creds();
+
+       if (!cred)
+               return -ENOMEM;
+
+       /*
+        * While by default tracefs is locked down, systems can be configured
+        * to allow user_event files to be less locked down. The extreme case
+        * being "other" has read/write access to user_events_data/status.
+        *
+        * When not locked down, processes may not have have permissions to
+        * add/remove calls themselves to tracefs. We need to temporarily
+        * switch to root file permission to allow for this scenario.
+        */
+       cred->fsuid = GLOBAL_ROOT_UID;
+
+       old_cred = override_creds(cred);
+
+       if (visible)
+               ret = trace_add_event_call(&user->call);
+       else
+               ret = trace_remove_event_call(&user->call);
+
+       revert_creds(old_cred);
+       put_cred(cred);
+
+       return ret;
+}
+
 static int destroy_user_event(struct user_event *user)
 {
        int ret = 0;
@@ -569,7 +604,7 @@ static int destroy_user_event(struct user_event *user)
        /* Must destroy fields before call removal */
        user_event_destroy_fields(user);
 
-       ret = trace_remove_event_call(&user->call);
+       ret = user_event_set_call_visible(user, false);
 
        if (ret)
                return ret;
@@ -1049,7 +1084,7 @@ static int user_event_trace_register(struct user_event *user)
        if (!ret)
                return -ENODEV;
 
-       ret = trace_add_event_call(&user->call);
+       ret = user_event_set_call_visible(user, true);
 
        if (ret)
                unregister_trace_event(&user->call.event);