Configuring

Now that we have a harnessed BIOS, we'll configure the fuzzer.

Enabling UEFI Tracking

During fuzzing, it will be helpful to us for many reasons if we can use source-level debugging functionality that is built into SIMICS. Recall that earlier, we made sure that the build directory inside our Docker container is the same as the directory we run our BIOS from. This is because we are going to use the UEFI Firmware Tracker built into SIMICS.

We already had a project/run.simics script, we'll create another script project/fuzz.simics which we'll build on to enable fuzzing.

We'll start with a script that just loads the platform and runs. We won't even be booting up to the UEFI shell, only through the BIOS image load process, so we'll remove the extra code that we had before.

load-target "qsp-x86/qsp-uefi-custom" namespace = qsp machine:hardware:firmware:bios = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"

run

Next, we want to add functionality to enable UEFI tracking, which you can read about in full detail in the docs.

At the top of the script, we'll load the tracker:

load-module uefi-fw-tracker

load-target "qsp-x86/qsp-uefi-custom" namespace = qsp machine:hardware:firmware:bios = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"

run

Then, we need to create a new OS-awareness object (which we'll call qsp.software), insert the UEFI tracker into the awareness module, and detect parameters, which we'll save to the file "%simics%/uefi.params". This params file will contain a dictionary of parameters like:

[
    'uefi_fw_tracker',
    {
        'tracker_version': 6263,
        'map_info': [],
        'map_file': None,
        'pre_dxe_start': 0,
        'pre_dxe_size': 0,
        'dxe_start': 0,
        'dxe_size': 4294967296,
        'exec_scan_size': 327680,
        'notification_tracking': True,
        'pre_dxe_tracking': False,
        'dxe_tracking': True,
        'hand_off_tracking': True,
        'smm_tracking': True,
        'reset_tracking': True,
        'exec_tracking': True
    }
]

We want to enable the map file, so we'll tell the command to set the map-file path to our map file. This will automatically populate the map_info with the info contained in the map file. Our script will look like this:

load-module uefi-fw-tracker

load-target "qsp-x86/qsp-uefi-custom" namespace = qsp machine:hardware:firmware:bios = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"

new-os-awareness name = qsp.software
qsp.software.insert-tracker tracker = uefi_fw_tracker_comp
qsp.software.tracker.detect-parameters -overwrite param-file = "%simics%/uefi.params" map-file = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/SimicsX58.map"
qsp.software.tracker.load-parameters "%simics%/uefi.params"
qsp.software.enable-tracker

run

With tracking enabled, we can add a source_location breakpoint on a symbol (SIMICS will track UEFI mappings and make symbols available when they are loaded during execution, or from a map file as we've done here). To break on assertions, we will add a breakpoint on the DebugAssert function (which EDK2's ASSERT macro ultimately calls).

Configuring the Fuzzer

The above can be applied to any code which runs during the SEC, PEI, or early DXE stages. If the codepath you want to fuzz is always executed during boot, all you need to do is add the harness macros to it and turn on the fuzzer.

We'll use the breakpoint API to wait for the DebugAssert function in a loop. We do this instead of using the $bp_num = bp.source_location.break DebugAssert command and adding it to the fuzzer configuration with @tsffs.breakpoints = [simenv.bp_num] because the HAP for breakpoints does not trigger on breakpoints set on source locations in this way, so the fuzzer cannot intercept it. This is in contrast to breakpoints set with the following, which will work with the tsffs API:

$ctx = (new-context)
qsp.mb.cpu0.core[0][0].set-context $ctx
$ctx.break -w $BUFFER_ADDRESS $BUFFER_SIZE

The rest of the configuration is similar to configuration we've already done in previous tutorials.

load-module tsffs
init-tsffs
tsffs.log-level 4
@tsffs.start_on_harness = True
@tsffs.stop_on_harness = True
@tsffs.timeout = 3.0
@tsffs.exceptions = [13, 14]

load-module uefi-fw-tracker

load-target "qsp-x86/qsp-uefi-custom" namespace = qsp machine:hardware:firmware:bios = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"

new-os-awareness name = qsp.software
qsp.software.insert-tracker tracker = uefi_fw_tracker_comp
qsp.software.tracker.detect-parameters -overwrite param-file = "%simics%/uefi.params" map-file = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/SimicsX58.map"
qsp.software.tracker.load-parameters "%simics%/uefi.params"
qsp.software.enable-tracker

script-branch {
    while 1 {
        bp.source_location.wait-for DebugAssert -x -error-not-planted
        echo "Got breakpoint"
        @tsffs.iface.fuzz.solution(1, "DebugAssert")
    }
}

run

Obtaining a Corpus

To keep things simple, we'll go ahead and use one file as the corpus provided to us, the actual boot image.

mkdir -p project/corpus/
curl -L -o project/corpus/0 https://raw.githubusercontent.com/tianocore/edk2-platforms/master/Platform/Intel/SimicsOpenBoardPkg/Logo/Logo.bmp