1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <bpf_helpers.h>
18 #include <linux/bpf.h>
19 #include <stdbool.h>
20 #include <stdint.h>
21 
22 // TODO: import this struct from generic header, access registers via generic
23 // function
24 struct pt_regs {
25   unsigned long regs[31];
26   unsigned long sp;
27   unsigned long pc;
28   unsigned long pr;
29   unsigned long sr;
30   unsigned long gbr;
31   unsigned long mach;
32   unsigned long macl;
33   long tra;
34 };
35 
36 struct SetUidTempAllowlistStateRecord {
37   __u64 uid;
38   bool onAllowlist;
39 };
40 
41 DEFINE_BPF_RINGBUF_EXT(output_buf, struct SetUidTempAllowlistStateRecord, 4096,
42                        AID_UPROBESTATS, AID_UPROBESTATS, 0600, "", "", PRIVATE,
43                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, LOAD_ON_ENG,
44                        LOAD_ON_USER, LOAD_ON_USERDEBUG);
45 
46 DEFINE_BPF_PROG("uprobe/set_uid_temp_allowlist_state", AID_UPROBESTATS,
47                 AID_UPROBESTATS, BPF_KPROBE2)
48 (struct pt_regs *ctx) {
49   struct SetUidTempAllowlistStateRecord *output = bpf_output_buf_reserve();
50   if (output == NULL)
51     return 1;
52   output->uid = ctx->regs[2];
53   output->onAllowlist = ctx->regs[3];
54   bpf_output_buf_submit(output);
55   return 0;
56 }
57 
58 struct jstring {
59   __u64 dummy;
60   __u32 count;
61   __u32 hash_code;
62 };
63 
64 struct UpdateDeviceIdleTempAllowlistRecord {
65   int changing_uid;
66   bool adding;
67   long duration_ms;
68   int type;
69   int reason_code;
70   char reason[256];
71   int calling_uid;
72 };
73 
74 DEFINE_BPF_RINGBUF_EXT(update_device_idle_temp_allowlist_records,
75                        struct UpdateDeviceIdleTempAllowlistRecord, 4096,
76                        AID_UPROBESTATS, AID_UPROBESTATS, 0600, "", "", PRIVATE,
77                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, LOAD_ON_ENG,
78                        LOAD_ON_USER, LOAD_ON_USERDEBUG);
79 
80 // Copies the string content of a Java String object located at <jstring> to
81 // <dest>.
recordString(void * jstring,unsigned int max_length,char * dest)82 void recordString(void *jstring, unsigned int max_length, char *dest) {
83   // Assumes the following memory layout of a Java String object:
84   // byte offset 8-11: count (this is the length of the string * 2)
85   // byte offset 12-15: hash_code
86   // byte offset 16 and beyond: string content
87   __u32 count;
88   bpf_probe_read_user(&count, sizeof(count), jstring + 8);
89   count /= 2;
90   bpf_probe_read_user_str(dest, max_length < count + 1 ? max_length : count + 1,
91                           jstring + 16);
92 }
93 
94 // Copies the content of a Java String object to <dest>, where the Java String
95 // is located at <position> in the method invocation argument list (0-based).
96 // This only works for the 0th - the 5th arguments. Rest of the arguments need
97 // to be accessed via stack pointer using the recordStringArgFromSp() function.
recordStringArg(struct pt_regs * ctx,unsigned int max_length,int position,char * dest)98 void recordStringArg(struct pt_regs *ctx, unsigned int max_length, int position,
99                      char *dest) {
100   recordString((void *)ctx->regs[2 + position], max_length, dest);
101 }
102 
103 // Copies the content of a Java String object to <dest>, where the Java String
104 // address is located in stack frame.
recordStringArgFromSp(struct pt_regs * ctx,unsigned int max_length,int sp_offset,char * dest)105 void recordStringArgFromSp(struct pt_regs *ctx, unsigned int max_length,
106                            int sp_offset, char *dest) {
107   void *jstring = NULL;
108   bpf_probe_read_user(&jstring, 4, (void *)ctx->sp + sp_offset);
109   recordString(jstring, max_length, dest);
110 }
111 
112 DEFINE_BPF_PROG("uprobe/update_device_idle_temp_allowlist", AID_UPROBESTATS,
113                 AID_UPROBESTATS, BPF_KPROBE3)
114 (struct pt_regs *ctx) {
115   struct UpdateDeviceIdleTempAllowlistRecord *output =
116       bpf_update_device_idle_temp_allowlist_records_reserve();
117   if (output == NULL)
118     return 1;
119 
120   // changing_uid is the 2nd argument, which is located in regs[3].
121   output->changing_uid = ctx->regs[3];
122   output->adding = ctx->regs[4];
123   output->duration_ms = ctx->regs[5];
124   output->type = ctx->regs[6];
125   output->reason_code = ctx->regs[7];
126 
127   // The <reason> argument is located at offset=40 in stack frame. This is
128   // calculated as 12 + sizeof(previous arguments). There are 6 preceding
129   // arguments all of which is 4 bytes each except for <long durationMs> which
130   // is 8 bytes. Therefore the offset is 12 + 5 * 4 + 8 = 40
131   recordStringArgFromSp(ctx, 256, 40, output->reason);
132 
133   // The <calling_uid> argument follows <reason> immediately and therefore has
134   // an offset that's 4 more bytes larger.
135   bpf_probe_read_user(&output->calling_uid, 4, (void *)ctx->sp + 44);
136 
137   bpf_update_device_idle_temp_allowlist_records_submit(output);
138   return 0;
139 }
140 
141 LICENSE("GPL");
142