Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ endif

all: host target
host: manager repro mutate prog2c db upgrade
target: execprog executor
target: execprog executor check_syzos

executor: descriptions
ifeq ($(TARGETOS),fuchsia)
Expand Down Expand Up @@ -427,6 +427,9 @@ check_links:
check_html:
./tools/check-html.sh

check_syzos: executor
./tools/check-syzos.sh 2>/dev/null

# Check that the diff is empty. This is meant to be executed after generating
# and formatting the code to make sure that everything is committed.
check_diff:
Expand Down
42 changes: 17 additions & 25 deletions executor/common_kvm_amd64_syzos.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,11 @@

// This file provides guest code running inside the AMD64 KVM.

#include "common_kvm_syzos.h"
#include "kvm.h"
#include <linux/kvm.h>
#include <stdbool.h>

// Host will map the code in this section into the guest address space.
#define GUEST_CODE __attribute__((section("guest")))

// Prevent function inlining. This attribute is applied to every guest_handle_* function,
// making sure they remain small so that the compiler does not attempt to be too clever
// (e.g. generate switch tables).
#define noinline __attribute__((noinline))

// Start/end of the guest section.
extern char *__start_guest, *__stop_guest;

// Compilers will eagerly try to transform the switch statement in guest_main()
// into a jump table, unless the cases are sparse enough.
// We use prime numbers multiplied by 10 to prevent this behavior.
Expand Down Expand Up @@ -185,29 +175,31 @@ GUEST_CODE static noinline void guest_handle_rdmsr(uint64 reg)
GUEST_CODE static noinline void guest_handle_wr_crn(struct api_call_2* cmd)
{
uint64 value = cmd->args[1];
switch (cmd->args[0]) {
case 0:
// Prevent the compiler from generating a switch table.
volatile uint64 reg = cmd->args[0];
if (reg == 0) {
// Move value to CR0.
asm volatile("movq %0, %%cr0" ::"r"(value) : "memory");
break;
case 2:
return;
}
if (reg == 2) {
// Move value to CR2.
asm volatile("movq %0, %%cr2" ::"r"(value) : "memory");
break;
case 3:
return;
}
if (reg == 3) {
// Move value to CR3.
asm volatile("movq %0, %%cr3" ::"r"(value) : "memory");
break;
case 4:
return;
}
if (reg == 4) {
// Move value to CR4.
asm volatile("movq %0, %%cr4" ::"r"(value) : "memory");
break;
case 8:
return;
}
if (reg == 8) {
// Move value to CR8 (TPR - Task Priority Register).
asm volatile("movq %0, %%cr8" ::"r"(value) : "memory");
break;
default:
// Do nothing.
break;
return;
}
}
15 changes: 3 additions & 12 deletions executor/common_kvm_arm64_syzos.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,11 @@

// This file provides guest code running inside the ARM64 KVM.

#include "common_kvm_syzos.h"
#include "kvm.h"
#include <linux/kvm.h>
#include <stdbool.h>

// Host will map the code in this section into the guest address space.
#define GUEST_CODE __attribute__((section("guest")))

// Prevent function inlining. This attribute is applied to every guest_handle_* function,
// making sure they remain small so that the compiler does not attempt to be too clever
// (e.g. generate switch tables).
#define noinline __attribute__((noinline))

// Start/end of the guest section.
extern char *__start_guest, *__stop_guest;

// Compilers will eagerly try to transform the switch statement in guest_main()
// into a jump table, unless the cases are sparse enough.
// We use prime numbers multiplied by 10 to prevent this behavior.
Expand Down Expand Up @@ -1201,7 +1191,8 @@ GUEST_CODE static void its_send_movall_cmd(uint64 cmdq_base, uint32 vcpu_id, uin
its_send_cmd(cmdq_base, &cmd);
}

GUEST_CODE static void its_send_invall_cmd(uint64 cmdq_base, uint32 collection_id)
GUEST_CODE static void
its_send_invall_cmd(uint64 cmdq_base, uint32 collection_id)
{
struct its_cmd_block cmd;
guest_memzero(&cmd, sizeof(cmd));
Expand Down
33 changes: 33 additions & 0 deletions executor/common_kvm_syzos.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2025 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Common SYZOS definitions.

// Prevent function inlining. This attribute is applied to every guest_handle_* function,
// making sure they remain small so that the compiler does not attempt to be too clever
// (e.g. generate switch tables).
#define noinline __attribute__((noinline))

// __no_stack_protector disables -fstack-protector which may introduce unwanted global accesses.
// TODO(glider): once syz-env-old migrates to GCC>11 we can just use
// __attribute__((no_stack_protector)).
#if defined(__clang__)
// Clang supports the no_stack_protector attribute.
#define __no_stack_protector __attribute__((no_stack_protector))
#elif defined(__GNUC__)
// The no_stack_protector attribute was introduced in GCC 11.1.
#if __GNUC__ > 11
#define __no_stack_protector __attribute__((no_stack_protector))
#else
// Fallback to the optimize attribute for older GCC versions.
#define __no_stack_protector __attribute__((__optimize__("-fno-stack-protector")))
#endif
#else
#define __no_stack_protector
#endif

// Host will map the code in this section into the guest address space.
#define GUEST_CODE __attribute__((section("guest"))) __no_stack_protector

// Start/end of the guest section.
extern char *__start_guest, *__stop_guest;
122 changes: 122 additions & 0 deletions tools/check-syzos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/bin/sh
# Copyright 2025 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
#
# This script scans the syz-executor binary for data relocations accesses
# within the "guest" ELF section that are problematic for the SYZOS guest
# code.
#
# It uses $TARGETOS and $TARGETARCH to locate the binary and determine the
# correct architecture.
#

set -e

SECTION_TO_CHECK="guest"

echoerr() {
echo "$@" >&2
}

if [ "$TARGETOS" != "linux" ]; then
echo "[INFO] TARGETOS is '$TARGETOS', not 'linux'. Skipping check."
exit 0
fi

if [ -z "$TARGETARCH" ]; then
echoerr "Error: \$TARGETARCH environment variable is not set."
exit 1
fi

BINARY="bin/${TARGETOS}_${TARGETARCH}/syz-executor"

if [ ! -f "$BINARY" ]; then
echoerr "Error: Binary not found at '$BINARY'"
exit 1
fi

echoerr "--> Analyzing architecture '$TARGETARCH'..."
OBJDUMP_CMD=""

if [ "$TARGETARCH" = "amd64" ]; then
ARCH="x86_64"
PATTERNS_TO_FIND='\\(%rip\\)'
if command -v x86_64-linux-gnu-objdump > /dev/null; then
OBJDUMP_CMD="x86_64-linux-gnu-objdump"
fi
elif [ "$TARGETARCH" = "arm64" ]; then
ARCH="aarch64"
PATTERNS_TO_FIND='adrp'
if command -v aarch64-linux-gnu-objdump > /dev/null; then
OBJDUMP_CMD="aarch64-linux-gnu-objdump"
fi
else
echo "[INFO] Unsupported architecture '$TARGETARCH', skipping check."
exit 0
fi
echoerr "--> Detected architecture: $ARCH"

if [ -z "$OBJDUMP_CMD" ]; then
echoerr "--> Arch-specific objdump not found, falling back to generic 'objdump'..."
if command -v objdump > /dev/null; then
OBJDUMP_CMD="objdump"
fi
fi

if [ -z "$OBJDUMP_CMD" ]; then
echoerr "Error: Could not find a usable objdump binary."
exit 1
fi
echoerr "--> Using objdump: $OBJDUMP_CMD"

echoerr "--> Verifying existence of section '$SECTION_TO_CHECK' in '$BINARY'..."
if ! "$OBJDUMP_CMD" -h --section="$SECTION_TO_CHECK" "$BINARY" >/dev/null 2>&1; then
echo
echo "[INFO] Section '$SECTION_TO_CHECK' not found in '$BINARY'. Skipping check."
exit 0
fi

echoerr "--> Disassembling section '$SECTION_TO_CHECK' and scanning for patterns ('$PATTERNS_TO_FIND')..."

DISASSEMBLY_STATUS=0
DISASSEMBLY_OUTPUT=$("$OBJDUMP_CMD" -d --section="$SECTION_TO_CHECK" "$BINARY" 2>/dev/null) || DISASSEMBLY_STATUS=$?

if [ $DISASSEMBLY_STATUS -ne 0 ]; then
echoerr "Error: '$OBJDUMP_CMD' failed to disassemble the '$SECTION_TO_CHECK' section."
# Attempt to show the actual error to the user
"$OBJDUMP_CMD" -d --section="$SECTION_TO_CHECK" "$BINARY" >/dev/null
exit 1
fi

FOUND_INSTRUCTIONS=$(echo "$DISASSEMBLY_OUTPUT" | awk -v pattern="$PATTERNS_TO_FIND" '
# Match a function header, e.g., "0000000000401136 <my_func>:"
/^[0-9a-f]+ <.*>:$/ {
match($0, /<.*>/)
current_func = substr($0, RSTART, RLENGTH)
}
# If the line matches the instruction pattern, print the context.
$0 ~ pattern {
if (current_func) {
print "In function " current_func ":"
}
print "\t" $0
}
' || true)

if [ -n "$FOUND_INSTRUCTIONS" ]; then
echo
echo "------------------------------------------------------------------"
echo "[FAIL] Found problematic data access instructions in '$SECTION_TO_CHECK'."
echo "The following instructions are likely to cause crashes in SyzOS:"
echo "$FOUND_INSTRUCTIONS" | sed 's/^/ /'
echo "------------------------------------------------------------------"
echo
echo "This typically happens when the C compiler emits read-only constants for"
echo "zero-initializing structs or for jump tables in switch statements."
exit 1
else
# Do not print anything to stdout unless there's an error.
echoerr
echoerr "[OK] No problematic data access instructions found in '$SECTION_TO_CHECK'."
exit 0
fi
Loading