/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define TLOG_TAG "sancov-rt" #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAGE_SIZE getauxval(AT_PAGESZ) typedef uint8_t counter_t; struct sancov_ctx { handle_t coverage_srv; size_t idx; struct cov_shm mailbox; struct cov_shm data; volatile struct coverage_record_header* headers; volatile counter_t* counters; volatile uintptr_t* pcs; size_t record_len; size_t num_counters; }; static bool in_sancov = false; #define SANCOV_START \ if (in_sancov) { \ return; \ } \ in_sancov = true; #define SANCOV_FINISH in_sancov = false; static size_t header_len(void) { return sizeof(struct coverage_record_header) + /* COV_START */ sizeof(struct coverage_record_header) + /* COV_8BIT_COUNTERS */ sizeof(struct coverage_record_header) + /* COV_INSTR_PCS */ sizeof(struct coverage_record_header); /* COV_TOTAL_LENGTH */ } static size_t counters_data_len(size_t num_counters) { return sizeof(counter_t) * num_counters; } static size_t pcs_data_len(size_t num_counters) { return sizeof(uintptr_t) * num_counters; } static size_t record_len(size_t num_counters) { return header_len() + counters_data_len(num_counters) + pcs_data_len(num_counters); } static void initialize_header(volatile struct coverage_record_header* headers, size_t num_counters) { uint32_t offset = header_len(); headers[1].type = COV_8BIT_COUNTERS; headers[1].offset = offset; offset += sizeof(counter_t) * num_counters; headers[2].type = COV_INSTR_PCS; headers[2].offset = offset; offset += sizeof(uintptr_t) * num_counters; headers[3].type = COV_TOTAL_LENGTH; headers[3].offset = offset; /* Mark the header as finished */ headers[0].offset = 0; headers[0].type = COV_START; } static int init(struct sancov_ctx* ctx, size_t num_counters) { int rc; handle_t chan; handle_t memref; struct coverage_aggregator_req req; struct coverage_aggregator_resp resp; rc = tipc_connect(&chan, COVERAGE_AGGREGATOR_PORT); if (rc != NO_ERROR) { TLOGE("failed (%d) to connect to coverage aggregator service\n", rc); return rc; } req.hdr.cmd = COVERAGE_AGGREGATOR_CMD_REGISTER; req.register_args.record_len = round_up(record_len(num_counters), PAGE_SIZE); rc = coverage_aggregator_rpc(chan, &req, NULL, &resp, &memref); if (rc != NO_ERROR) { TLOGE("failed (%d) coverage aggregator RPC\n", rc); goto err_rpc; } rc = cov_shm_mmap(&ctx->mailbox, memref, resp.register_args.mailbox_len); if (rc != NO_ERROR) { TLOGE("failed to mmap() mailbox shared memory\n"); goto err_mmap; } ctx->num_counters = num_counters; ctx->record_len = record_len(num_counters); ctx->coverage_srv = chan; ctx->idx = resp.register_args.idx; close(memref); return NO_ERROR; err_mmap: close(memref); err_rpc: close(chan); return rc; } static int get_record(struct sancov_ctx* ctx) { int rc; handle_t memref; struct coverage_aggregator_req req; struct coverage_aggregator_resp resp; size_t shm_len; if (cov_shm_is_mapped(&ctx->data)) { cov_shm_munmap(&ctx->data); } ctx->counters = NULL; ctx->pcs = NULL; req.hdr.cmd = COVERAGE_AGGREGATOR_CMD_GET_RECORD; rc = coverage_aggregator_rpc(ctx->coverage_srv, &req, NULL, &resp, &memref); if (rc != NO_ERROR) { TLOGE("failed (%d) coverage aggregator RPC\n", rc); return rc; } shm_len = resp.get_record_args.shm_len; if (shm_len < ctx->record_len) { TLOGE("not enough shared memory, received: %zu, need at least: %zu\n", shm_len, ctx->record_len); rc = ERR_BAD_LEN; goto out; } rc = cov_shm_mmap(&ctx->data, memref, resp.get_record_args.shm_len); if (rc != NO_ERROR) { TLOGE("failed to mmap() coverage record shared memory\n"); goto out; } ctx->headers = ctx->data.base; initialize_header(ctx->headers, ctx->num_counters); ctx->counters = ctx->data.base + header_len(); ctx->pcs = ctx->data.base + header_len() + counters_data_len(ctx->num_counters); rc = NO_ERROR; out: close(memref); return rc; } static void update_record(struct sancov_ctx* ctx, size_t idx, uintptr_t pc) { assert(idx < ctx->num_counters); /* * Since counters are fixed-sized, there is always a chance of overflowing. * Cap maximum counter value instead of overflowing. */ if (ctx->counters[idx] < (counter_t)(-1)) { ctx->counters[idx]++; } if (!ctx->pcs[idx]) { ctx->pcs[idx] = pc - getauxval(AT_BASE); } } static int get_event(struct sancov_ctx* ctx) { int* app_mailbox = (int*)(ctx->mailbox.base) + ctx->idx; int event = READ_ONCE(*app_mailbox); WRITE_ONCE(*app_mailbox, COVERAGE_MAILBOX_EMPTY); return event; }; static struct sancov_ctx ctx; __attribute__((__weak__)) void __sanitizer_cov_trace_pc_guard_init( uint32_t* start, uint32_t* stop) { SANCOV_START; static size_t num_counters = 0; int rc; /* Initialize only once */ if (start == stop || *start) { goto out; } for (uint32_t* x = start; x < stop; x++) { *x = ++num_counters; } TLOGI("sancov initialized with %lu counters\n", num_counters); rc = init(&ctx, num_counters * sizeof(counter_t)); assert(rc == NO_ERROR); out: SANCOV_FINISH; } __attribute__((__weak__)) void __sanitizer_cov_trace_pc_guard(uint32_t* guard) { SANCOV_START; int rc; int event = get_event(&ctx); /* Guards start at 1, and indices start at 0 */ assert(*guard > 0); size_t idx = *guard - 1; switch (event) { case COVERAGE_MAILBOX_EMPTY: break; case COVERAGE_MAILBOX_RECORD_READY: rc = get_record(&ctx); assert(rc == NO_ERROR); break; default: TLOGE("unknown event: %d\n", event); abort(); } if (cov_shm_is_mapped(&ctx.data)) { uintptr_t ret_address = (uintptr_t)__builtin_return_address(0); /* The sancov tool expects the address of the instruction before the * call to this function on ARM and AArch64. */ #if defined(__aarch64__) ret_address -= 4; #elif defined(__arm__) ret_address = (ret_address - 3) & (~1); #else #error Only ARM and AArch64 are supported by the Trusty sancov runtime #endif update_record(&ctx, idx, ret_address); } SANCOV_FINISH; }