| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
Follow-up fix for https://github.com/google/syzkaller/pull/6820
|
| |
|
|
|
|
|
|
|
|
| |
L1 guest memory is non-contiguous, but previously host setup assumed
the opposite, using L1 guest addresses as offsets in the host memory
block. This led to subtle bugs in IRQ handling (and possibly elsewhere).
Fix this by using gpa_to_hva() to translate guest physical addresses to
host virtual addresses.
This function is cold, so we can afford O(SYZOS_REGION_COUNT) complexity.
|
| |
|
|
|
|
|
|
|
|
| |
Moving setup_pg_table() before setup_gdt_64() prevents the page table
initialization from accidentally erasing the newly created Global
Descriptor Table (GDT).
If the GDT is zeroed out, the CPU hardware cannot fetch the necessary
code segment descriptors to deliver interrupts or exceptions, leading
to unhandled #GP or #DF crashes.
|
| |
|
|
| |
This is only needed for tests generated by prog2c.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit corrects the GDT setup for the data and TSS segments in L1.
Previously, the data segment was incorrectly using the TSS base address,
and the TSS base address was not properly set.
The data segment base is now set to 0, as it should be for a flat 64-bit
model. The TSS segment descriptor in the GDT now correctly points to
X86_SYZOS_ADDR_VAR_TSS and uses the full 64-bit address.
The attributes are also updated to mark the TSS as busy.
Additionally, the TSS region is now explicitly copied from L1 to L2 to
ensure the L2 environment has a valid TSS.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit introduces the `SYZOS_API_NESTED_LOAD_SYZOS` command to
enable running full SYZOS programs within a nested L2 guest, enhancing
fuzzing capabilities for nested virtualization.
Key changes include:
- Nested SYZOS Execution: The new command loads a SYZOS program into an
L2 VM, setting up its execution environment.
- ABI Refinement: Program size is now passed via the shared `syzos_globals`
memory region instead of registers, standardizing the ABI for L1 and L2.
- L2 State Management: Improved saving and restoring of L2 guest GPRs
across VM-exits using inline assembly wrappers for Intel and AMD.
- Nested UEXIT Propagation: Intercepts EPT/NPT faults on the exit page to
capture the L2 exit code from saved registers and forward it to L0 with
an incremented nesting level.
- L2 Memory Management: Updates to L2 page table setup, including skipping
NO_HOST_MEM regions to force exits, and a new `l2_gpa_to_pa` helper.
|
| |
|
|
|
| |
When setting up L1 guest, execute CPUID and enable X86_EFER_SVME for
AMD CPUs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Refactor the SYZOS L1 guest to construct L2 page tables dynamically by
mirroring its own memory layout (provided via boot arguments) instead
of using a static 2MB identity map.
This change introduces l2_map_page to allocate unique backing memory
for most regions, while mapping X86_SYZOS_ADDR_USER_CODE and
X86_SYZOS_ADDR_STACK_BOTTOM to specific per-VM buffers reserved in L1.
This allows L1 to inject code and stack content into backing buffers
while the L2 guest executes them from standard virtual addresses.
Additionally, MEM_REGION_FLAG_* definitions are moved to the guest
header to support this logic.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Enable the SYZOS guest (L1) to dynamically allocate memory
for nested L2 page tables, replacing the previous rigid static layout.
Move the mem_region and syzos_boot_args struct definitions to the guest
header (common_kvm_amd64_syzos.h) to allow the guest to parse the memory
map injected by the host.
Introduce a bump allocator, guest_alloc_page(), which targets the
X86_SYZOS_ADDR_UNUSED heap. This allocator relies on a new struct
syzos_globals located at X86_SYZOS_ADDR_GLOBALS to track the allocation
offset.
Refactor setup_l2_page_tables() to allocate intermediate paging levels
(PDPT, PD, PT) via guest_alloc_page() instead of using fixed contiguous
offsets relative to the PML4. This allows for disjoint memory usage and
supports future recursion requirements.
|
| |
|
|
|
|
|
|
|
|
| |
Reserve a dedicated 4KB page at X86_SYZOS_ADDR_GLOBALS (0x17F000) to
store global state shared across the SYZOS L1 guest environment.
This region is required to store the state of the guest-side memory
allocator (specifically the allocation offset and total size of the
unused heap), enabling thread-safe dynamic memory allocation for nested
L2 page tables.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Introduce a dedicated page at X86_SYZOS_ADDR_BOOT_ARGS to pass
configuration data from the executor to the SYZOS guest. This will allow
dynamic adjustments to the guest environment, such as specifying memory
region sizes.
- Added `MEM_REGION_FLAG_REMAINING` to flag the last memory region, which
will consume the rest of the available guest memory.
- Defined `struct syzos_boot_args` to pass the memory region layout to the
guest.
- Modified `syzos_mem_regions`:
- Reduced X86_SYZOS_ADDR_VAR_IDT size to 10 pages.
- Inserted the new X86_SYZOS_ADDR_BOOT_ARGS region.
- Added a final region with MEM_REGION_FLAG_REMAINING.
- Updated `setup_vm` to:
- Calculate the size of the REMAINING region.
- Populate the `syzos_boot_args` structure in the boot args page.
- Updated `setup_pg_table` to use the REMAINING flag to map the last region.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Refactor the SYZOS guest memory layout to decouple the dynamic page table
allocator from the fixed system data structures (GDT, IDT, initial PML4).
Previously, the page table pool was located at 0x5000, tightly packed with
the initial system pages. This rigid structure made it difficult to expand
the pool or inject configuration data without shifting fixed offsets.
Move X86_SYZOS_ADDR_PT_POOL to 0x180000, creating a distinct high-memory
region well above the L2 VCPU data, and increase the pool size to 64 pages
(256KB) to support deeper nested hierarchies.
Update the syz_kvm_setup_syzos_vm logic to handle non-contiguous
Guest-to-Host address translation via a new get_host_pte_ptr() helper.
This is necessary because the executor's host memory allocation remains
strictly linear while the guest physical address space now contains
significant gaps.
This layout change is a prerequisite for enabling "SYZOS inside SYZOS"
(L2 nesting), allowing the future injection of boot arguments into the
gap created between fixed data and dynamic regions.
|
| |
|
|
|
|
|
|
| |
Enhance the debugging capabilities of C reproducers by passing the VCPU
file descriptor to the syz_kvm_assert_syzos_uexit function. With access to
the VCPU fd, the function can now dump the VCPU's register state upon
assertion failure, providing critical context for debugging guest execution
issues.
|
| |
|
|
| |
Set up the L1 guest's 64-bit Task State Segment (TSS), a prerequisite for VMX/SVM.
|
| |
|
|
|
|
|
|
|
|
|
| |
This patch lays the groundwork for nested virtualization by rearranging
the KVM guest's memory map.
Key changes include:
- Introducing a dedicated per-VCPU memory region for L2 VMs.
- Updating `executor/kvm.h` with:
- Adjusted stack addresses for the L1 guest.
- Detailed memory layout macros for L2 VM structures
|
| |
|
|
|
| |
Not having these results in three copies of every KVM-related #define
in each reproducer.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Apply __addrspace_guest to every guest function and use a C++ template
to statically validate that host functions are not passed to
executor_fn_guest_addr().
This only works in Clang builds of syz-executor, because GCC does not
support address spaces, and C reproducers cannot use templates.
The static check allows us to drop the dynamic checks in DEFINE_GUEST_FN_TO_GPA_FN().
While at it, replace DEFINE_GUEST_FN_TO_GPA_FN() with explicit declarations of
host_fn_guest_addr() and guest_fn_guest_addr().
|
| |
|
|
|
| |
Use SYZOS_ADDR_EXECUTOR_CODE instead of both. Also put platform-specific
definitions under #if GOARCH_xxx.
|
| |
|
|
| |
Make sure setup_cpuid() is only declared together with install_user_code()
|
| |
|
|
|
|
|
|
|
|
| |
The new API call allows to initialize the handler with one of the
three possible values:
- NULL (should cause a page fault)
- dummy_null_handler (should call iret)
- uexit_irq_handler (should perform guest_uexit(UEXIT_IRQ))
Also add a test for uexit_irq_handler()
|
| |
|
|
|
|
|
|
| |
Use a pool of 32 pages to allocate PT and PE entries for the guest
page tables.
This eliminates the need for manually assigned page table entries
that are brittle and may break when someone changes the memory
layout.
|
| |
|
|
|
| |
Pass around struct kvm_syzos_vm instead of one-off pointers to
various guest memory ranges.
|
| |
|
|
|
| |
Untangle SYZOS GDT setup from the legacy one.
Drop LDT and TSS for now.
|
| |
|
|
|
| |
Per https://wiki.osdev.org/Task_State_Segment#Long_Mode,
io_bitmap and reserved3 should be 16-bit.
|
| |
|
|
|
| |
Instead of open-coding every memory region in several places,
use a single array to configure their creation.
|
| |
|
|
|
|
| |
Provide map_4k_region() to ease page table creation for different
regions.
While at it, also move the stack from 0x0 to 0x90000.
|
| |
|
|
|
| |
DEFINE_GUEST_FN_TO_GPA_FN() allows to define helper functions to
calculate guest addresses in the host/guest code.
|
| |
|
|
|
|
|
| |
To distinguish SYZOS addresses from other x86 definitions, change them
to start with X86_SYZOS_ADDR_
No functional change.
|
| |
|
|
| |
Implement a pseudo-syscall to check the value of kvm_run.exit_reason
|
| |
|
|
|
|
| |
This commit adds the actual SyzOS fuzzer for x86-64 and a small test. It
also updates some necessary parts of the ARM version and adds some glue
for i386.
|
| |
|
|
|
| |
This commit prepares adding the X86-64 SYZOS by declaring the relevant
functions, updating their ARM64 versions and adding placeholders.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* Fixes a bug when setting up a 64-bit guest by making the bit
manipulation macros produce unsigned long long: To create a VCPU that
has paging enabled, one needs to set the CR0.PE and CR0.PG bits in
CR0. The latter is problematic when setting up a 64-bit guest since if
the macro is not using 1ULL, it sign extends the output (in 64-bit
mode the control registers are extended to 64-bits with some of the
CR0[32:63] bits reserved). This results in either failing the
KVM_SET_SREGS ioctl (in newer kernel versions) or just failing the
KVM_RUN ioctl with EXIT_REASON_INVALID_STATE.
* Moved the bit manipulation definitions from the amd64 specific to the generic
kvm header to consolidate them with the already existing ones.
Prefixed them with X86_ to avoid confusion.
|
| |
|
|
|
|
|
|
| |
The "avl" fields (variable type is u8) of the kvm_segment structure variables such as
seg_cs16 and seg_ldt are not initialized to zero. During creation, there is a chance that
they are set to values other than 0 or 1, which can cause the "avl" fields to overwrite
other fields when executing the fill_segment_descriptor function, leading to erroneous
results.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
It is impossible to compile a number of definitions in
include/uapi/linux/kvm.h for other platforms, which leads
to syz-extract failing to update constants.
Skip processing of this file for all arches except i386 and
amd64.
This is a hacky and (hopefully) temporary solution until #2754
is implemented.
|
| |
|
|
|
|
|
| |
At the moment only AMD64 is supported, change file names to emphasise
this.
Signed-off-by: Alexey Kardashevskiy <aik@linux.ibm.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We generally use the newer C99 var declarations combined with initialization because:
- declarations are more local, reduced scope
- fewer lines of code
- less potential for using uninit vars and other bugs
However, we have some relic code from times when we did not understand
if we need to stick with C89 or not. Also some external contributions
that don't follow style around.
Add a static check for C89-style declarations and fix existing precedents.
Akaros toolchain uses -std=gnu89 (or something) and does not allow
variable declarations inside of for init statement. And we can't switch
it to -std=c99 because Akaros headers are C89 themselves.
So in common.h we need to declare loop counters outside of for.
|
| |
|
|
|
|
|
|
|
|
| |
GCC10 fails to build the code with errors:
executor/common_kvm_amd64.h:143:64: error: ‘gate.kvm_segment::type’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
executor/common_kvm_amd64.h:143:56: error: ‘gate.kvm_segment::base’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
Replace 'case 6' with 'case 5' since 'i % 6' results in [0..5].
Signed-off-by: Denis Efremov <efremov@linux.com>
|
| |
|
|
|
|
| |
This is not needed anymore afer the previous commit.
Fixes #1918
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The added test triggers warnings like these:
<stdin>: In function ‘syz_mount_image.constprop’:
<stdin>:298:3: error: argument 1 null where non-null expected [-Werror=nonnull]
In file included from <stdin>:26:0:
/usr/include/x86_64-linux-gnu/sys/stat.h:320:12: note: in a call to function ‘mkdir’ declared here
extern int mkdir (const char *__path, __mode_t __mode)
^~~~~
cc1: all warnings being treated as errors
<stdin>: In function ‘syz_open_procfs.constprop’:
<stdin>:530:41: error: ‘%s’ directive argument is null [-Werror=format-truncation=]
<stdin>:85:110: note: in definition of macro ‘NONFAILING’
<stdin>:532:41: error: ‘%s’ directive argument is null [-Werror=format-truncation=]
<stdin>:85:110: note: in definition of macro ‘NONFAILING’
<stdin>:534:41: error: ‘%s’ directive argument is null [-Werror=format-truncation=]
<stdin>:85:110: note: in definition of macro ‘NONFAILING’
Use volatile for all arguments of syz_ functions to prevent
compiler from treating the arguments as constants in reproducers.
Popped up during bisection that used a repro that previously worked.
Update #501
|
| |
|
|
|
| |
We don't frequently debug it and it does some intensive computations
on coverage, so no reason to not compile with -O2.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The "define uint64_t unsigned long long" were too good to work.
With a different toolchain I am getting:
cstdint:69:11: error: expected unqualified-id
using ::uint64_t;
^
executor/common.h:34:18: note: expanded from macro 'uint64_t'
Do it the proper way: introduce uint64/32/16/8 types and use them.
pkg/csource then does s/uint64/uint64_t/ to not clutter code with
additional typedefs.
|
| | |
|
| | |
|
| |
|
|
|
|
| |
Allow fuzzer to change types of segment descriptors.
Alter more flags.
Allow fuzzer to do a random vmwrite.
|
| |
|
|
|
|
|
|
|
|
|
| |
Fuzzer has figured out how to corrupt input/output shmem regions
abusing the text memcpy in syz_kvm_setup_cpu. It guessed a negative
text_size value that causes the memcpy to overwrite shmem regions.
Protect better against such cases:
1. Make text_size unsigned (there is already a check that it is less than 1000).
2. Map input region as readable only, we don't write to it.
3. Add address sanity check to segv_handler, if we see that we are writing
into executable data, it's better to crash instantly.
|
|
|
1. Basic support for arm64 kvm testing.
2. Fix compiler warnings in x86 kvm code.
3. Test all pseudo syz calls in csource.
4. Fix handling of real code in x86.
|