platform_bionic/tests/libs/cfi_test_lib.cpp
Evgenii Stepanov 071416b700 Use PROT_NONE on the unused parts of CFI shadow.
This replaces a single 2Gb readable memory region with a bunch of tiny
regions, and leaves the bulk of 2Gb mapped but unaccessible. This makes
it harder to defeat ASLR by probing for the CFI shadow region.

Sample CFI shadow mapping with this change:
7165151000-716541f000 ---p 00000000 00:00 0                              [anon:cfi shadow]
716541f000-7165420000 r--p 00000000 00:00 0                              [anon:cfi shadow]
7165420000-71654db000 ---p 00000000 00:00 0                              [anon:cfi shadow]
71654db000-71654dc000 r--p 00000000 00:00 0                              [anon:cfi shadow]
71654dc000-71654dd000 r--p 00000000 00:00 0                              [anon:cfi shadow]
71654dd000-71654f0000 ---p 00000000 00:00 0                              [anon:cfi shadow]
71654f0000-71654f1000 r--p 00000000 00:00 0                              [anon:cfi shadow]
71654f1000-71e5151000 ---p 00000000 00:00 0                              [anon:cfi shadow]

This change degrades CFI diagnostics for wild jumps and casts (i.e. when
the target of a CFI check is outside of any known library bounds). This
is acceptable, because CFI does not have much to tell about those cases
anyway. Such bugs will show up as SEGV_ACCERR crashes inside
__cfi_slowpath in libdl.so from now on.

Bug: 158113540
Test: bionic-unit-tests/cfi_test.*
Test: adb shell cat /proc/$PID/maps | grep cfi

Change-Id: I57cbd0d3f87eb1610ad99b48d98ffd497ba214b4
Merged-In: I57cbd0d3f87eb1610ad99b48d98ffd497ba214b4
2020-06-12 12:22:32 -07:00

84 lines
2.3 KiB
C++

/*
* Copyright (C) 2017 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.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
// This library is built for all targets, including host tests, so __cfi_slowpath may not be
// present. But it is only used in the bionic loader tests.
extern "C" __attribute__((weak)) void __cfi_slowpath(uint64_t, void*);
static size_t g_count;
static uint64_t g_last_type_id;
static void* g_last_address;
static void* g_last_diag;
extern "C" {
// Make sure the library crosses at least one kLibraryAlignment(=256KB) boundary.
char bss[1024 * 1024];
// Mock a CFI-enabled library without relying on the compiler.
__attribute__((aligned(4096))) void __cfi_check(uint64_t CallSiteTypeId, void* TargetAddr,
void* Diag) {
++g_count;
g_last_type_id = CallSiteTypeId;
g_last_address = TargetAddr;
g_last_diag = Diag;
}
size_t get_count() {
return g_count;
}
uint64_t get_last_type_id() {
return g_last_type_id;
}
void* get_last_address() {
return g_last_address;
}
void* get_last_diag() {
return g_last_diag;
}
void* get_global_address() {
return &g_count;
}
}
// Check that CFI is set up in module constructors and destructors.
struct A {
void check_cfi_self() {
g_last_type_id = 0;
assert(&__cfi_slowpath);
// CFI check for an address inside this DSO. This goes to the current module's __cfi_check,
// which updates g_last_type_id.
__cfi_slowpath(13, static_cast<void*>(&g_last_type_id));
assert(g_last_type_id == 13);
// CFI check for a libc function. This never goes into this module's __cfi_check, and must pass.
__cfi_slowpath(14, reinterpret_cast<void*>(&exit));
assert(g_last_type_id == 13);
}
A() {
check_cfi_self();
}
~A() {
check_cfi_self();
}
} a;