blob: 438cd0270b54f54289b9701d98f732690214ce97 [file] [log] [blame]
<html devsite>
<head>
<title>Building a Pixel kernel with KASAN+KCOV</title>
<meta name="project_path" value="/_project.yaml" />
<meta name="book_path" value="/_book.yaml" />
</head>
<body>
<!--
Copyright 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.
-->
<p>
Kernel Address Sanitizer (<a
href="https://www.kernel.org/doc/html/latest/dev-tools/kasan.html">KASAN</a>)
helps kernel developers and testers find runtime memory-related bugs, such as
out-of-bound read or write operations, and use-after-free issues. While KASAN
isn't enabled on production builds due to its runtime performance
penalties and memory usage increment, it is still a valuable tool for testing
debug builds.
</p>
<p>
When used with another runtime tool called Kernel Coverage (<a
href="https://lwn.net/Articles/671640/">KCOV</a>), KASAN-sanitized and
KCOV-instrumented code helps developers and testers to detect runtime memory
errors and obtain code coverage information. In the scenario of kernel fuzz
testing, e.g. via <a href="https://github.com/google/syzkaller">syzkaller</a>,
KASAN helps to determine the root cause of crashes, and KCOV provides code
coverage information to the fuzzing engine to help in test-case or corpus
deduplication.
</p>
<p>
This page does not discuss the inner workings or mechanics of KASAN. Rather, it
serves as a guide to build and modify the Android Open Soure Project (AOSP) and
Pixel's kernel source to boot with KASAN and KCOV turned on.
</p>
<h2 id="setting-up-your-build-environment">Setting up your build
environment</h2>
<p>
Follow the steps in the <a
href="/setup/requirements">Downloading and
Building</a> section to set up your build environment.
</p>
<h2 id="building-aosp">Building AOSP</h2>
<p>
Download the <a href="/setup/downloading">Android source code</a>. For the
purpose of building KASAN images, choose a stable build that is not in active
development. Often, the latest available release/stable branch is a good choice.
More information about build and branch can be found at <a
href="/setup/build-numbers#source-code-tags-and-builds">Source
Code Tags and Builds</a>.
</p>
<p>
After you have successfully checked out the source code, download the necessary
device blobs that correspond to the device and branch you are using from <a
href="https://developers.google.com/android/drivers">Driver Binaries for
Nexus and Pixel Devices</a>. Download both the vendor image and the set of
binaries from the System-on-Chip (SOC) manufacturer. Then, unarchive the
downloaded tarballs, run the scripts they contain, and accept the licenses.
</p>
<aside class="note">
<strong>Tip</strong>: Double check that you have the <a
href="/setup/initializing#installing-the-jdk">right
version of JDK</a> installed on your system before proceeding further.
</aside>
<p>
Then clean up, set up your build environment, and choose your build target,
following the steps in
<a href="/setup/building#cleaning-up">Preparing to Build</a>.
</p>
<p>
To establish a working base, make your first build without modifications:
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal" data-terminal-prefix="~/src/aosp$ ">make -j48</code>
</pre>
<p>
Flash your build result to a test device (for example, marlin) and let it boot:
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal" data-terminal-prefix="~/src/aosp$ ">cd out/target/product/marlin</code>
<code class="devsite-terminal" data-terminal-prefix="~/src/aosp/out/target/product/marlin$ ">ANDROID_PRODUCT_OUT=`pwd` fastboot flashall -w</code>
</pre>
<p>
After booting to the homescreen, you might see a pop-up that says:
</p>
<p>
<code>There's an internal problem with your device. Contact your manufacturer
for details.</code> This pop-up likely means that the build fingerprint of your
vendor and your system partition do not match. Because this build is just for
development and testing, and not a release build, just ignore it.
</p>
<h2 id="building-the-kernel">Building the kernel</h2>
<p>To build the kernel, you need to check out the correct source code,
cross-compile it, and then build the kernel image in the correct AOSP
directory.</p>
<h3 id="checking-out-kernel-source-code">Checking out kernel source code</h3>
<p>
Create a directory to store the kernel source code and clone the AOSP kernel git
repository to your local storage.
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy">mkdir ~/src/marlin-kernel-src</code>
<code class="devsite-terminal devsite-click-to-copy">cd ~/src/marlin-kernel-src</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/marlin-kernel-src$ ">git clone https://android.googlesource.com/kernel/msm</code>
</pre>
<p>
After you are done, there should be an empty directory named <code>msm</code>.
</p>
<p>
Enter the <code>msm</code> directory and <code>git checkout</code> the branch
that corresponds to the source code you are building. For the list of available
branches and tags, see the <a href="https://android.googlesource.com/kernel/msm/">Android msm
kernel source tree</a>.
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/marlin-kernel-src$ ">cd msm</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/marlin-kernel-src$ ">git checkout <var>TAG_NAME</var></code>
</pre>
<p>
After completing this step, the <code>msm</code> directory should have content.
</p>
<h3 id="performing-cross-compilation">Performing cross compilation</h3>
<p>
Next you need to compile the Android kernel.
</p>
<h5 id="setting-up-your-cross-compiler">Setting up your cross-compiler</h5>
<p>
To build the kernel, you need to set up a cross-compiler. The current
recommended and tested toolchain is Android's NDK toolchain latest stable
version. To download the Android NDK, visit the official <a
href="https://developer.android.com/ndk/downloads/index.html">Android NDK
website</a>. Download the appropriate zip archive for your platform, and unzip
it. This results in a directory resembling
<code>android-ndk-<var>NDK_VERSION</var></code>.
</p>
<h5 id="downloading-the-lz4c-tool">Downloading the LZ4c tool</h5>
<p>
The Pixel kernel uses <a hre="//lz4.github.io/lz4/">LZ4 compression</a>,
so the <code>lz4c</code> tool is required when you build your kernel. If you
use Ubuntu, install the <code>lz4c</code> tool by:
</p>
<pre class="devsite-terminal devsite-click-to-copy">sudo apt-get install liblz4-tool
</pre>
<h4 id="building-your-kernel">Building your kernel</h4>
<p>
Set up your build environment from the <code>marlin-kernel-src/msm</code>
directory with:
</p>
<pre>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">export ARCH=arm64</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">export CROSS_COMPILE=<var>PATH_TO_NDK</var>/android-ndk-<var>NDK_VERSION</var>/toolchains/aarch64-linux-android-<var>TOOLCHAIN_VERSION</var>/prebuilt/linux-x86_64/bin/aarch64-linux-android-</code>
</pre>
<p>
Then build an unmodified version of your kernel to establish a working base:
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">make marlin_defconfig</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">make -j48</code>
</pre>
<p>
The result of the build process can be found at:
<code>arch/arm64/boot/Image.lz4-dtb</code>
</p>
<h4 id="rebuilding-the-boot-image-in-aosp">Rebuilding the boot image in
AOSP</h4>
<p>
After you have built the kernel image, copy the result over into AOSP's
<code>device/google/marlin-kernel</code> directory, with:
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp$ ">cp ${marlin-kernel-src}/msm/arch/arm64/boot/Image.lz4-dtb device/google/marlin-kernel</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp$ ">source build/envsetup.sh</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp$ ">lunch aosp_marlin-userdebug</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp$ ">make -j48</code>
</pre>
<p>
After a successful build, flash the target device with:
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp$ ">cd out/target/product/marlin</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp/out/target/product/marlin$ ">fastboot flashall -w</code>
</pre>
<p>
After flashing, your device should boot. Verify the image you flashed to
the device is the kernel image you built by checking <code>Kernel
version</code> under <code>Settings -&gt; System -&gt; About phone</code>
after the device finishes booting.
</p>
<h2 id="modifying-the-kernel">Modifying the kernel</h2>
<h3 id="enabling-kasan-and-kcov-compile-options">Enabling KASAN and KCOV compile
options</h3>
<p>
KASAN and KCOV codes are guarded by compilation flags, which are not turned on
for normal builds. To enable them, add KASAN and KCOV options to the config
file, but drop the LZ4 config.
</p>
<p>
To do this, make a copy of the default config file, for example,
<code>marlin_defconfig</code>:
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">cd arch/arm64/configs</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm/arch/arm64/configs$ ">cp marlin_defconfig marlin-kasan_defconfig</code>
</pre>
<p>
In the new config file, remove this flag <code>CONFIG_KERNEL_LZ4=y</code> and
add these flags:
</p>
<pre class="devsite-click-to-copy">CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y
CONFIG_KCOV=y
CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=y
</pre>
<h2 id="recompiling-the-kernel-with-new-configuration">Recompiling the kernel
with new configuration</h2>
<p>
After you've finished modifying your copy of the config file, recompile the
kernel.
</p>
<h3 id="reconfiguring-the-kernel">Reconfiguring the kernel</h3>
<p>
Set up your <a href="/setup/building-kernels#building">build environment</a>.
Build your modified <code>defconfig</code> and check if the newly added flags
are present in the produced <code>.config</code> file.
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">make marlin-kasan_defconfig</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">grep KASAN .config
CONFIG_HAVE_ARCH_<strong>KASAN</strong>=y
CONFIG_<strong>KASAN</strong>=y
# CONFIG_<strong>KASAN</strong>_OUTLINE is not set
CONFIG_<strong>KASAN</strong>_INLINE=y</code>
</pre>
<p>
You should see the KASAN flags. Compile your kernel:
</p>
<pre class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">make -j48
</pre>
<h3 id="checking-the-modified-kernel-image">Checking the modified kernel
image</h3>
<p>
After a successful compilation, navigate to the <code>arch/arm64/boot</code>
directory to view the compilation results. Generally, the
<code>Image.gz-dtb</code> is about 23MB and larger than that of a standard build.
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm$ ">cd arch/arm64/boot</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src/msm/arch/arm64/boot$ ">ls -lh Image.gz-dtb
-rw-r--r-- 1 username groupname 23M Aug 11 13:59 Image.gz-dtb</code>
</pre>
<p>
To see if KCOV was properly compiled, perform additional analysis on the
produced <code>vmlinux</code> at the root of the kernel source tree. If you run
an <code>objdump</code> on vmlinux, you should see numerous calls to
<code>__sanitizer_cov_trace_pc()</code>.
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="marlin-kernel-src$ ">sh -c '${CROSS_COMPILE}objdump -d vmlinux' | grep sanitizer
ffffffc000082030: 94040658 bl ffffffc000183990 &lt;__sanitizer_cov_trace_pc&gt;
ffffffc000082050: 94040650 bl ffffffc000183990 &lt;__sanitizer_cov_trace_pc&gt;
ffffffc000082078: 94040646 bl ffffffc000183990 &lt;__sanitizer_cov_trace_pc&gt;
ffffffc000082080: 94040644 bl ffffffc000183990 &lt;__sanitizer_cov_trace_pc&gt;
ffffffc0000820ac: 94040639 bl ffffffc000183990 &lt;__sanitizer_cov_trace_pc&gt;
</code></pre>
<h2 id="modifying-aosp-code">Modifying AOSP code</h2>
<p>
Before plugging in the new boot image, you need to adjust certain parameters in
AOSP's source code that govern how the device boots. This is mainly necessary to
ensure the new (inflated) image boots properly.
</p>
<h3 id="adjusting-board-parameters">Adjusting board parameters</h3>
<p>
Adjust the boot parameters defined in the device's <code>BoardConfig.mk</code>
file. It is located at <code>device/google/marlin/marlin</code> relative to the
root of your AOSP source code.
</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp$ ">cd device/google/marlin/marlin</code>
<code class="devsite-terminal devsite-click-to-copy" data-terminal-prefix="~/src/aosp/device/google/marlin/marlin$ ">vim BoardConfig.mk</code>
</pre>
<aside class="caution">
<p>
<strong>Caution</strong>: Make sure you have a backup of the original
<code>BoardConfig.mk</code> file before proceeding, in case something goes
wrong.
</p>
<p>
The adjustments to be made can be summarized as follows through a
<code>git diff</code> result:
</p>
<pre>diff --git a/marlin/BoardConfig.mk b/marlin/BoardConfig.mk
index 31533fb9..81caf05d 100644
--- a/marlin/BoardConfig.mk
+++ b/marlin/BoardConfig.mk
@@ -116,15 +116,10 @@ BOARD_EGL_CFG := device/google/marlin/egl.cfg
BOARD_KERNEL_BASE := 0x80000000
BOARD_KERNEL_PAGESIZE := 4096
<var>-ifneq ($(filter marlin_kasan, $(TARGET_PRODUCT)),)</var>
BOARD_KERNEL_OFFSET := 0x80000
BOARD_KERNEL_TAGS_OFFSET := 0x02500000
BOARD_RAMDISK_OFFSET := 0x02700000
BOARD_MKBOOTIMG_ARGS := --kernel_offset $(BOARD_KERNEL_OFFSET) --ramdisk_offset $(BOARD_RAMDISK_OFFSET) --tags_offset $(BOARD_KERNEL_TAGS_OFFSET)
<var>-else
-BOARD_KERNEL_TAGS_OFFSET := 0x02000000
-BOARD_RAMDISK_OFFSET := 0x02200000
-endif</var>
TARGET_KERNEL_ARCH := arm64
TARGET_KERNEL_HEADER_ARCH := arm64
</pre>
</aside>
<p>
If you do not wish to modify the <code>BoardConfig.mk</code>
file, you could instead create a new lunch target that contains the name
<code>marlin_kasan</code>. For more information about this process, see
<a href="/setup/add-device">Adding a New Device</a>.
</p>
<h3 id="adjusting-the-kernel-target-in-the-local-makefile">Adjusting the kernel
target in the local Makefile</h3>
<p>
The new kernel uses LZ4 compression to improve speed, but KASAN requires gzip
for better compression ratio. To accommodate this, tell the build machinery
which kernel to bundle into the final target by modifying where the
<code>LOCAL_KERNEL</code> variable points to in
<code>device/google/marlin/device-common.mk</code>.
</p>
<h2 id="rebuilding-boot-image">Rebuilding the boot image</h2>
<p>
To rebuild the boot image, copy the new kernel image into the AOSP tree in the
device-specific folder (e.g. <code>device/google/marlin-kernel</code>). Make
sure this is where the build system expects the kernel target image to be
at, according to how you modified it earlier.
</p>
<p>
Next, rebuild your flashable images, similar to how you <a
href="#building-aosp">built AOSP</a> earlier. Upon successful build, flash all
built images as usual.
</p>
<h2 id="booting-your-device-with-a-modified-kernel-image">Booting your device
with a modified kernel image</h2>
<p>
You should now have a build that boots and enters the home screen. From here,
check the device's <code>dmesg</code> output for a "<code>KernelAddressSanitizer
initialized</code>" message in the very early boot stage. That message means
KASAN is initialized during boot time. Also, you can confirm
<code>/sys/kernel/debug/kcov</code> is present on the device (you will have to
be root to do that).
</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<p>
You can experiment with different kernel versions, starting with a standard
build as a working base, before turning on KASAN+KCOV compile options. When
things break, first check if the bootloader and baseband version on your device
matches those required by the new build. Finally, you might have to
catch up with a newer branch of the Android tree altogether if you venture too
far ahead with the kernel version.
</p>
</body>
</html>