• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #define TLOG_TAG "sancov-rt"
18 
19 #include <assert.h>
20 #include <interface/coverage/aggregator.h>
21 #include <lib/coverage/common/ipc.h>
22 #include <lib/coverage/common/record.h>
23 #include <lib/coverage/common/cov_shm.h>
24 #include <lib/tipc/tipc.h>
25 #include <lk/macros.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <sys/auxv.h>
29 #include <sys/mman.h>
30 #include <trusty_log.h>
31 #include <uapi/err.h>
32 
33 #define PAGE_SIZE getauxval(AT_PAGESZ)
34 
35 typedef uint8_t counter_t;
36 
37 struct sancov_ctx {
38     handle_t coverage_srv;
39     size_t idx;
40     struct cov_shm mailbox;
41     struct cov_shm data;
42     volatile struct coverage_record_header* headers;
43     volatile counter_t* counters;
44     volatile uintptr_t* pcs;
45     size_t record_len;
46     size_t num_counters;
47 };
48 
49 static bool in_sancov = false;
50 
51 #define SANCOV_START \
52     if (in_sancov) { \
53         return;      \
54     }                \
55     in_sancov = true;
56 
57 #define SANCOV_FINISH in_sancov = false;
58 
header_len(void)59 static size_t header_len(void) {
60     return sizeof(struct coverage_record_header) + /* COV_START */
61            sizeof(struct coverage_record_header) + /* COV_8BIT_COUNTERS */
62            sizeof(struct coverage_record_header) + /* COV_INSTR_PCS */
63            sizeof(struct coverage_record_header);  /* COV_TOTAL_LENGTH */
64 }
65 
counters_data_len(size_t num_counters)66 static size_t counters_data_len(size_t num_counters) {
67     return sizeof(counter_t) * num_counters;
68 }
69 
pcs_data_len(size_t num_counters)70 static size_t pcs_data_len(size_t num_counters) {
71     return sizeof(uintptr_t) * num_counters;
72 }
73 
record_len(size_t num_counters)74 static size_t record_len(size_t num_counters) {
75     return header_len() + counters_data_len(num_counters) +
76            pcs_data_len(num_counters);
77 }
78 
initialize_header(volatile struct coverage_record_header * headers,size_t num_counters)79 static void initialize_header(volatile struct coverage_record_header* headers,
80                               size_t num_counters) {
81     uint32_t offset = header_len();
82     headers[1].type = COV_8BIT_COUNTERS;
83     headers[1].offset = offset;
84     offset += sizeof(counter_t) * num_counters;
85     headers[2].type = COV_INSTR_PCS;
86     headers[2].offset = offset;
87     offset += sizeof(uintptr_t) * num_counters;
88     headers[3].type = COV_TOTAL_LENGTH;
89     headers[3].offset = offset;
90 
91     /* Mark the header as finished */
92     headers[0].offset = 0;
93     headers[0].type = COV_START;
94 }
95 
init(struct sancov_ctx * ctx,size_t num_counters)96 static int init(struct sancov_ctx* ctx, size_t num_counters) {
97     int rc;
98     handle_t chan;
99     handle_t memref;
100     struct coverage_aggregator_req req;
101     struct coverage_aggregator_resp resp;
102 
103     rc = tipc_connect(&chan, COVERAGE_AGGREGATOR_PORT);
104     if (rc != NO_ERROR) {
105         TLOGE("failed (%d) to connect to coverage aggregator service\n", rc);
106         return rc;
107     }
108 
109     req.hdr.cmd = COVERAGE_AGGREGATOR_CMD_REGISTER;
110     req.register_args.record_len = round_up(record_len(num_counters), PAGE_SIZE);
111 
112     rc = coverage_aggregator_rpc(chan, &req, NULL, &resp, &memref);
113     if (rc != NO_ERROR) {
114         TLOGE("failed (%d) coverage aggregator RPC\n", rc);
115         goto err_rpc;
116     }
117 
118     rc = cov_shm_mmap(&ctx->mailbox, memref, resp.register_args.mailbox_len);
119     if (rc != NO_ERROR) {
120         TLOGE("failed to mmap() mailbox shared memory\n");
121         goto err_mmap;
122     }
123 
124     ctx->num_counters = num_counters;
125     ctx->record_len = record_len(num_counters);
126     ctx->coverage_srv = chan;
127     ctx->idx = resp.register_args.idx;
128 
129     close(memref);
130     return NO_ERROR;
131 
132 err_mmap:
133     close(memref);
134 err_rpc:
135     close(chan);
136     return rc;
137 }
138 
get_record(struct sancov_ctx * ctx)139 static int get_record(struct sancov_ctx* ctx) {
140     int rc;
141     handle_t memref;
142     struct coverage_aggregator_req req;
143     struct coverage_aggregator_resp resp;
144     size_t shm_len;
145 
146     if (cov_shm_is_mapped(&ctx->data)) {
147         cov_shm_munmap(&ctx->data);
148     }
149     ctx->counters = NULL;
150     ctx->pcs = NULL;
151 
152     req.hdr.cmd = COVERAGE_AGGREGATOR_CMD_GET_RECORD;
153 
154     rc = coverage_aggregator_rpc(ctx->coverage_srv, &req, NULL, &resp, &memref);
155     if (rc != NO_ERROR) {
156         TLOGE("failed (%d) coverage aggregator RPC\n", rc);
157         return rc;
158     }
159     shm_len = resp.get_record_args.shm_len;
160 
161     if (shm_len < ctx->record_len) {
162         TLOGE("not enough shared memory, received: %zu, need at least: %zu\n",
163               shm_len, ctx->record_len);
164         rc = ERR_BAD_LEN;
165         goto out;
166     }
167 
168     rc = cov_shm_mmap(&ctx->data, memref, resp.get_record_args.shm_len);
169     if (rc != NO_ERROR) {
170         TLOGE("failed to mmap() coverage record shared memory\n");
171         goto out;
172     }
173 
174     ctx->headers = ctx->data.base;
175     initialize_header(ctx->headers, ctx->num_counters);
176 
177     ctx->counters = ctx->data.base + header_len();
178     ctx->pcs = ctx->data.base + header_len() +
179                counters_data_len(ctx->num_counters);
180     rc = NO_ERROR;
181 
182 out:
183     close(memref);
184     return rc;
185 }
186 
update_record(struct sancov_ctx * ctx,size_t idx,uintptr_t pc)187 static void update_record(struct sancov_ctx* ctx, size_t idx, uintptr_t pc) {
188     assert(idx < ctx->num_counters);
189     /*
190      * Since counters are fixed-sized, there is always a chance of overflowing.
191      * Cap maximum counter value instead of overflowing.
192      */
193     if (ctx->counters[idx] < (counter_t)(-1)) {
194         ctx->counters[idx]++;
195     }
196     if (!ctx->pcs[idx]) {
197         ctx->pcs[idx] = pc - getauxval(AT_BASE);
198     }
199 }
200 
get_event(struct sancov_ctx * ctx)201 static int get_event(struct sancov_ctx* ctx) {
202     int* app_mailbox = (int*)(ctx->mailbox.base) + ctx->idx;
203     int event = READ_ONCE(*app_mailbox);
204     WRITE_ONCE(*app_mailbox, COVERAGE_MAILBOX_EMPTY);
205     return event;
206 };
207 
208 static struct sancov_ctx ctx;
209 
__sanitizer_cov_trace_pc_guard_init(uint32_t * start,uint32_t * stop)210 __attribute__((__weak__)) void __sanitizer_cov_trace_pc_guard_init(
211         uint32_t* start,
212         uint32_t* stop) {
213     SANCOV_START;
214 
215     static size_t num_counters = 0;
216     int rc;
217 
218     /* Initialize only once */
219     if (start == stop || *start) {
220         goto out;
221     }
222 
223     for (uint32_t* x = start; x < stop; x++) {
224         *x = ++num_counters;
225     }
226 
227     TLOGI("sancov initialized with %lu counters\n", num_counters);
228 
229     rc = init(&ctx, num_counters * sizeof(counter_t));
230     assert(rc == NO_ERROR);
231 
232 out:
233     SANCOV_FINISH;
234 }
235 
__sanitizer_cov_trace_pc_guard(uint32_t * guard)236 __attribute__((__weak__)) void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
237     SANCOV_START;
238 
239     int rc;
240     int event = get_event(&ctx);
241 
242     /* Guards start at 1, and indices start at 0 */
243     assert(*guard > 0);
244     size_t idx = *guard - 1;
245 
246     switch (event) {
247     case COVERAGE_MAILBOX_EMPTY:
248         break;
249 
250     case COVERAGE_MAILBOX_RECORD_READY:
251         rc = get_record(&ctx);
252         assert(rc == NO_ERROR);
253         break;
254 
255     default:
256         TLOGE("unknown event: %d\n", event);
257         abort();
258     }
259 
260     if (cov_shm_is_mapped(&ctx.data)) {
261         uintptr_t ret_address = (uintptr_t)__builtin_return_address(0);
262         /* The sancov tool expects the address of the instruction before the
263          * call to this function on ARM and AArch64. */
264 #if defined(__aarch64__)
265         ret_address -= 4;
266 #elif defined(__arm__)
267         ret_address = (ret_address - 3) & (~1);
268 #else
269 #error Only ARM and AArch64 are supported by the Trusty sancov runtime
270 #endif
271         update_record(&ctx, idx, ret_address);
272     }
273 
274     SANCOV_FINISH;
275 }
276