diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..2cb7564 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,116 @@ +build --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host +build --crosstool_top=@llvm_toolchain//:toolchain + +# TODO(nedwill): remove this if possible so we can build Docker images +# on non-Linux AMD64 platforms +build --@io_bazel_rules_docker//transitions:enable=false + +# Force the use of Clang for C++ builds. +build --action_env=CC=clang +build --action_env=CXX=clang++ +build --linkopt=-fuse-ld=mold +# build --action_env=BAZEL_CXXOPTS="-std=c++20" +build --cxxopt=-std=c++20 +build --host_cxxopt=-std=c++20 + +# Use CWD for running binaries +# run --run_under='bash -c "cd \"$BUILD_WORKING_DIRECTORY\" && exec"' + +# Test output settings +test --test_output=errors +test --test_summary=terse + +# TODO(nedwill): remove this +build --copt=-mllvm +build --copt=-asan-stack=0 + +# Needed for fast unwinding +build --copt=-fno-omit-frame-pointer + +# For Centipede stack trace support +build --copt=-mcrc32 + +# TODO(nedwill): we should not use this when building for oss-fuzz +build --copt=-march=native + +# Ignore some warnings, including warnings about unknown warnings. +# TODO(nedwill): Use OSS-Fuzz compiler everywhere so we can avoid no-unknown-warning-option. +build --copt=-Wno-unknown-warning-option +build --copt=-Wno-deprecated-builtins +build --per_file_copt=^(third_party/.*|external/.*)$@-Wno-deprecated-pragma +build --per_file_copt=^(third_party/.*|external/.*)$@-Wno-deprecated-non-prototype +build --per_file_copt=^(third_party/.*|external/.*)$@-Wno-fortify-source + +build --copt=-ggdb +build --copt=-gdwarf-aranges + +# Debug information and path mapping settings +# These settings affect how debug information is generated and how paths are recorded +# in the debug symbols. They interact with the VS Code debugger configuration. +# Note: These settings, particularly the debug-prefix-map, work in conjunction with +# the 'set substitute-path' command in the VS Code launch configuration. If debugging +# issues arise, consider adjusting both this setting and the VS Code configuration. +# +# Known issue: The combination of these settings and the VS Code debugger may result +# in paths with '././' prefixes, which is addressed in the launch.json configuration. +# This issue has only been observed when running midas-rr remotely via the Remote SSH +# extension for VS Code. +build --copt=-fdebug-prefix-map=/proc/self/cwd=. +build --features=oso_prefix_is_pwd + +# Fix build graph blowup by aligning normal build settings with those used +# in cc_fuzz_test. +build --dynamic_mode=off + +# Used for building visual coverage reports +build:clang-coverage --copt=-fprofile-instr-generate +build:clang-coverage --copt=-fcoverage-mapping +build:clang-coverage --linkopt=-fprofile-instr-generate +build:clang-coverage --platform_suffix=clang-coverage + +# valgrind performance profiling +build:valgrind --copt=-DLIMIT_CALLGRIND_SCOPE +build:valgrind --copt=-O3 +build:valgrind --copt=-ggdb + +# Strip instrumentation from non-target code +# TODO(nedwill): just negate the /third_party/xnu folder +# TODO(nedwill): consider selective instrumentation for different targets +build:libfuzzer --per_file_copt=third_party/libprotobuf-mutator/src/.*,concurrence/.*,fuzz/.*,external/com_google_protobuf/.*,.*\.pb\.cc,external/com_google_absl/.*,external/com_google_googletest/.*,external/centipede/.*,-external/centipede/puzzles/.*,-external/centipede/.*fuzz_target@-fsanitize-coverage=0 + +# Strip sanitizers from uninteresting code +# build:libfuzzer --per_file_copt=third_party/libprotobuf-mutator/src/.*,concurrence/.*,fuzz/.*,external/com_google_protobuf/.*,.*\.pb\.cc,external/com_google_absl/.*,external/com_google_googletest/.*,external/centipede/.*,-external/centipede/puzzles/.*,-external/centipede/.*fuzz_target@-fno-sanitize=address\\,memory\\,undefined + +build:asan --copt=-fsanitize=address +build:asan --linkopt=-fsanitize=address +build:asan --platform_suffix=asan +test:asan --test_env=ASAN_OPTIONS=detect_leaks=0 + +# Should these be for all builds or just opt builds? +build:opt --per_file_copt='\\.pb\\.cc$@-g0' +build:opt --per_file_copt=.*\.pb\.cc@-fsanitize-coverage=0 +build:opt --per_file_copt=concurrence/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=external/com_google_absl/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=external/com_google_googletest/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=external/com_google_protobuf/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=external/zlib/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=fuzz/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=fuzz/common/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=fuzz/common/mig_types/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=fuzz/xnu/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=third_party/backward-cpp/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=third_party/bootstrap_cmds/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=third_party/libco/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=third_party/libprotobuf-mutator/src/.*@-fsanitize-coverage=0 +build:opt --per_file_copt=tools/generate_syscall/.*@-fsanitize-coverage=0 + +# No Centipede except test binaries +build:centipede --per_file_copt=external/centipede/.*,-external/centipede/puzzles/.*,-external/centipede/.*fuzz_target@-fsanitize-coverage=0 +build:centipede --platform_suffix=centipede + +# Enforce stricter environment rules, which eliminates some non-hermetic +# behavior and therefore improves both the remote cache hit rate and the +# correctness and repeatability of the build. +build --incompatible_strict_action_env=true +# TODO(nedwill): this will become default in Bazel 7 but breaks crosstool_top +# build --incompatible_enable_cc_toolchain_resolution diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..f22d756 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +6.5.0 diff --git a/.clang-tidy b/.clang-tidy deleted file mode 100644 index a1fc134..0000000 --- a/.clang-tidy +++ /dev/null @@ -1,32 +0,0 @@ -Checks: > - *, - -altera-*, - -bugprone-easily-swappable-parameters, - -bugprone-reserved-identifier, - -cert-*, - -cppcoreguidelines-*, - -fuchsia-*, - -hicpp-*, - -llvm-*, - -llvmlibc-*, - -google-objc-function-naming, - -misc-const-correctness, - -misc-no-recursion, - -misc-non-private-member-variables-in-classes, - -misc-unused-parameters, - -modernize-avoid-c-arrays, - -modernize-deprecated-headers, - -modernize-use-nodiscard, - -modernize-use-trailing-return-type, - -modernize-use-using, - -performance-no-int-to-ptr, - -readability-function-cognitive-complexity, - -readability-identifier-length, - -readability-implicit-bool-conversion, - -readability-magic-numbers, - -WarningsAsErrors: '' -HeaderFilterRegex: '' -AnalyzeTemporaryDtors: false -FormatStyle: none -User: sockfuzzer diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 4690677..0000000 --- a/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -* -!bsd -!CMakeLists.txt -!build.sh -!fuzz -!libkern -!security -!protobuf-3.7.1 -!BUILD -!EXTERNAL_HEADERS -!osfmk diff --git a/.gitignore b/.gitignore index eaf78ba..11f69de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,6 @@ -.vscode -__pycache__ *.o *.cpo *.cpd *.d *.filelist -/build - -### Automatically added by Hedron's Bazel Compile Commands Extractor: https://github.com/hedronvision/bazel-compile-commands-extractor -# Ignore the `external` link (that is added by `bazel-compile-commands-extractor`). The link differs between macOS/Linux and Windows, so it shouldn't be checked in. The pattern must not end with a trailing `/` because it's a symlink on macOS/Linux. -/external -# Ignore links to Bazel's output. The pattern needs the `*` because people can change the name of the directory into which your repository is cloned (changing the `bazel-` symlink), and must not end with a trailing `/` because it's a symlink on macOS/Linux. -/bazel-* -# Ignore generated output. Although valuable (after all, the primary purpose of `bazel-compile-commands-extractor` is to produce `compile_commands.json`!), it should not be checked in. -/compile_commands.json -# Ignore the directory in which `clangd` stores its local index. /.cache/ diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..16d3a73 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,26 @@ +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +# TODO(nedwill): this is needed to make fuzz target visible to other paths. +# Move fuzz targets to their own non-root package and make that publicly visible. +package(default_visibility = ["//visibility:public"]) + +config_setting( + name = "asan_enabled", + values = {"copt": "-fsanitize=address"}, +) + +refresh_compile_commands( + name = "refresh_compile_commands", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + "//...": "", + "@fuzztest//...": "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 2379375..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,672 +0,0 @@ -# Copyright 2021 Google LLC -# -# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. The rights granted to you under the License -# may not be used to create, or enable the creation or redistribution of, -# unlawful or unlicensed copies of an Apple operating system, or to -# circumvent, violate, or enable the circumvention or violation of, any -# terms of an Apple operating system software license agreement. -# -# Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - -cmake_minimum_required(VERSION 3.5.1) -project(sockfuzzer) - -# TODO(nedwill): We should not depend directly on third_party module paths -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/third_party/libprotobuf-mutator/cmake/external) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(Protobuf_USE_STATIC_LIBS true) - -# Force colorful output -add_compile_options(-fcolor-diagnostics) - -set(XNU_DEFINES - -DAH_ALL_CRYPTO - -DAPPLE - -DBINDFS - -DCONFIG_32BIT_TELEMETRY - -DCONFIG_AIO_MAX=90 - -DCONFIG_AIO_PROCESS_MAX=16 - -DCONFIG_AIO_THREAD_COUNT=4 - -DCONFIG_APPLEDOUBLE - -DCONFIG_ARCADE - -DCONFIG_ATM - -DCONFIG_BACKGROUND_QUEUE - -DCONFIG_BASESYSTEMROOT - -DCONFIG_BLOCKS - -DCONFIG_COALITIONS - -DCONFIG_CODE_DECRYPTION - -DCONFIG_COREDUMP - -DCONFIG_CSR - -DCONFIG_DATALESS_FILES - -DCONFIG_DELAY_IDLE_SLEEP - -DCONFIG_DEV_KMEM - -DCONFIG_DIRTYSTATUS_TRACKING - -DCONFIG_DYNAMIC_CODE_SIGNING - -DCONFIG_EXT_RESOLVER - -DCONFIG_FIRMLINKS - -DCONFIG_GZALLOC - -DCONFIG_ICMP_BANDLIM=250 - -DCONFIG_IMAGEBOOT_CHUNKLIST - -DCONFIG_IMG4 - -DCONFIG_IMGSRC_ACCESS - -DCONFIG_IOSCHED - -DCONFIG_IPC_TABLE_ENTRIES_STEPS=256 - -DCONFIG_KAS_INFO - -DCONFIG_KDP_INTERACTIVE_DEBUGGING - -DCONFIG_KEC_FIPS - -DCONFIG_KEXT_BASEMENT - -DCONFIG_KEYPAGE_WP - -DCONFIG_KN_HASHSIZE=64 - -DCONFIG_LEDGER_INTERVAL_MAX - -DCONFIG_LTABLE_DEBUG - -DCONFIG_LTABLE_STATS - -DCONFIG_MACF_LAZY_VNODE_LABELS - -DCONFIG_MACH_APPROXIMATE_TIME - -DCONFIG_MACH_BRIDGE_SEND_TIME - -DCONFIG_MAXVIFS=32 - -DCONFIG_MAX_CLUSTERS=8 - -DCONFIG_MAX_THREADS=500 - -DCONFIG_MBUF_JUMBO - -DCONFIG_MCA - -DCONFIG_MEMORYSTATUS - -DCONFIG_MFCTBLSIZ=256 - -DCONFIG_MIN_NBUF=256 - -DCONFIG_MIN_NIOBUF=128 - -DCONFIG_MNT_ROOTSNAP - -DCONFIG_MNT_SUID - -DCONFIG_MOUNT_PREBOOTRECOVERY - -DCONFIG_MOUNT_VM - -DCONFIG_MSG_BSIZE=CONFIG_MSG_BSIZE_DEV - -DCONFIG_MSG_BSIZE_DEV=131072 - -DCONFIG_MSG_BSIZE_REL=16384 - -DCONFIG_MTRR - -DCONFIG_NC_HASH=4096 - -DCONFIG_NETBOOT - -DCONFIG_NFS_GSS - -DCONFIG_NMBCLUSTERS=512 - -DCONFIG_PERSONAS - -DCONFIG_PHYS_WRITE_ACCT - -DCONFIG_PROC_UDATA_STORAGE - -DCONFIG_PROC_UUID_POLICY - -DCONFIG_PROTECT - -DCONFIG_REQUIRES_U32_MUNGING - -DCONFIG_ROSV_STARTUP - -DCONFIG_SCHED_GRRR - -DCONFIG_SCHED_GRRR_CORE - -DCONFIG_SCHED_MULTIQ - -DCONFIG_SCHED_PROTO - -DCONFIG_SCHED_SFI - -DCONFIG_SCHED_TIMESHARE_CORE - -DCONFIG_SCHED_TRADITIONAL - -DCONFIG_SEARCHFS - -DCONFIG_SETUID - -DCONFIG_SLEEP - -DCONFIG_SYSDIAGNOSE - -DCONFIG_TASKWATCH - -DCONFIG_TASK_MAX=1024 - -DCONFIG_TASK_ZONE_INFO - -DCONFIG_TCBHASHSIZE=4096 - -DCONFIG_TELEMETRY - -DCONFIG_THREAD_MAX=2560 - -DCONFIG_VFS_NAMES=4096 - -DCONFIG_VMX - -DCONFIG_VNGUARD - -DCONFIG_VNODES=263168 - -DCONFIG_VOLFS - -DCONFIG_VSPRINTF - -DCONFIG_WAITQ_DEBUG - -DCONFIG_WAITQ_STATS - -DCONFIG_WORKLOOP_DEBUG - -DCONFIG_WORKQUEUE - -DCONFIG_XNUPOST - -DCONFIG_ZALLOC_SEQUESTER - -DCONFIG_ZCACHE - -DCONFIG_ZLEAKS - -DCONFIG_ZLEAK_ALLOCATION_MAP_NUM=16384 - -DCONFIG_ZLEAK_TRACE_MAP_NUM=8192 - -DCONFIG_ZONE_MAP_MIN=120586240 - -DCONTENT_FILTER - -DCOPYOUT_SHIM - -DCRYPTO_SHA2 - -DFLOW_DIVERT - -DHYPERVISOR - -DICMP_BANDLIM - -DIFNET_INPUT_SANITY_CHK - -DIMPORTANCE_TRACE - -DIOKIT - -DIOKITCPP - -DIOKITSTATS - -DIOTRACKING - -DIPSEC - -DIPSEC_ESP - -DKERNEL - -DKERNEL_PRIVATE - -DKPC - -DKPERF - -DLIBKERNCPP - -DLOCK_STATS - -DLP64 - -DMACH - -DMACH_BSD - -DMACH_KERNEL - -DMULTICAST - -DMULTIPATH - -DNAMEDSTREAMS - -DNECP - -DNO_DIRECT_RPC - -DNULLFS - -DOLD_SEMWAIT_SIGNAL - -DPACKET_MANGLER - -DPAGE_SIZE_FIXED - -DPAL_I386 - -DPGO - -DPLATFORM_MacOSX - -DPRIVATE - -DPSYNCH - -DSERIAL_CONSOLE - -DTRAFFIC_MGT - -DVIDEO_CONSOLE - -DVM_PRESSURE_EVENTS - -DX86_64 - -DXNU_KERNEL_PRIVATE - -DXNU_KERN_EVENT_DATA_IS_VLA - # TODO(nedwill): remove this flag and get support for copyin_chk/copyout_chk - # working in case source fortifications can help find more bugs. - -D_FORTIFY_SOURCE=0 - -D__MACHO__=1 - -D__X86_64__ - -Dvolatile=__volatile - -Dx86_64 - -URC_ENABLE_XNU_PRODUCT_INFO_FILTER - -include - meta_features.h -) - -set(BSD_DEFINES - -DBSD_BUILD - -DBSD_KERNEL_PRIVATE - -DCONFIG_SERIAL_KDP - -DDEBUG - -DDRIVER_PRIVATE - -DENCRYPTED_SWAP - -DHIBERNATION - -DKERNEL_BUILD - -DLP64_DEBUG=0 - -DMACH_KDP - -DMACH_MP_DEBUG - -DTARGET_OS_IPHONE=0 - -DTARGET_OS_OSX=1 - -DXNU_TARGET_OS_OSX=1 - -D_KERNEL_BUILD - -D__APPLE__ -) - -set(OSFMK_DEFINES - -DBOND - -DCONFIG_AUDIT - -DCONFIG_FSE - -DCONFIG_IMAGEBOOT - -DCONFIG_MACF - -DCONFIG_MACF_SOCKET_SUBSET - -DCONFIG_NFS4 - -DCONFIG_TRIGGERS - -DDEVFS - -DDUMMYNET - -DFDESC - -DFIFO - -DFS_COMPRESSION - -DIF_BRIDGE - -DIF_FAKE - -DIF_HEADLESS - -DINET - -DKERNEL_BASE_OFFSET=0x100000 - -DMACH_COMPAT - -DMACH_FASTLINK - -DMACH_KERNEL_PRIVATE - -DMPTCP - -DNETWORKING - -DNFSCLIENT - -DNFSSERVER - -DPF - -DPFLOG - -DQUOTA - -DSENDFILE - -DSIXLOWPAN - -DSOCKETS - -DSYSV_MSG - -DSYSV_SEM - -DSYSV_SHM - -DVLAN - -DXNU_TARGET_OS_OSX - -DZLIB -) - -set(SAN_DEFINES -DKASAN_OFFSET=0 -DKASAN_SCALE=0) - -set(XNU_WARNING_FLAGS - -Wno-address-of-packed-member -Wno-unguarded-availability-new - -Wno-pointer-to-int-cast -Wno-format -Wunused-result -Werror -) - -set(XNU_C_FLAGS - -nostdinc - -fno-builtin - -fno-common - -fsigned-bitfields - -g - -fblocks - -O1 - -ftrivial-auto-var-init=pattern - ${XNU_WARNING_FLAGS} - ${XNU_DEFINES} -) - -set(BSD_SOURCES - third_party/xnu/bsd/conf/param.c - third_party/xnu/bsd/kern/kern_asl.c - third_party/xnu/bsd/kern/kern_control.c - third_party/xnu/bsd/kern/kern_descrip.c - third_party/xnu/bsd/kern/kern_event.c - third_party/xnu/bsd/kern/kern_newsysctl.c - third_party/xnu/bsd/kern/kern_sig.c - third_party/xnu/bsd/kern/kern_subr.c - third_party/xnu/bsd/kern/kpi_mbuf.c - third_party/xnu/bsd/kern/kpi_socket.c - third_party/xnu/bsd/kern/kpi_socketfilter.c - third_party/xnu/bsd/kern/subr_eventhandler.c - third_party/xnu/bsd/kern/sys_domain.c - third_party/xnu/bsd/kern/sys_generic.c - third_party/xnu/bsd/kern/sys_pipe.c - third_party/xnu/bsd/kern/sys_reason.c - third_party/xnu/bsd/kern/sys_socket.c - third_party/xnu/bsd/kern/uipc_domain.c - third_party/xnu/bsd/kern/uipc_mbuf.c - third_party/xnu/bsd/kern/uipc_mbuf2.c - third_party/xnu/bsd/kern/uipc_proto.c - third_party/xnu/bsd/kern/uipc_socket.c - third_party/xnu/bsd/kern/uipc_socket2.c - third_party/xnu/bsd/kern/uipc_syscalls.c - third_party/xnu/bsd/kern/uipc_usrreq.c - third_party/xnu/bsd/kern/vsock_domain.c - third_party/xnu/bsd/net/bpf.c - third_party/xnu/bsd/net/bpf_filter.c - third_party/xnu/bsd/net/classq/classq.c - third_party/xnu/bsd/net/classq/classq_fq_codel.c - third_party/xnu/bsd/net/classq/classq_sfb.c - third_party/xnu/bsd/net/classq/classq_subr.c - third_party/xnu/bsd/net/content_filter.c - third_party/xnu/bsd/net/devtimer.c - third_party/xnu/bsd/net/dlil.c - third_party/xnu/bsd/net/ether_inet6_pr_module.c - third_party/xnu/bsd/net/ether_if_module.c - third_party/xnu/bsd/net/ether_inet_pr_module.c - third_party/xnu/bsd/net/flowadv.c - third_party/xnu/bsd/net/frame802154.c - third_party/xnu/bsd/net/if.c - third_party/xnu/bsd/net/if_6lowpan.c - third_party/xnu/bsd/net/if_bridge.c - third_party/xnu/bsd/net/if_bond.c - third_party/xnu/bsd/net/if_fake.c - third_party/xnu/bsd/net/if_llatbl.c - third_party/xnu/bsd/net/if_llreach.c - third_party/xnu/bsd/net/if_loop.c - third_party/xnu/bsd/net/if_low_power_mode.c - third_party/xnu/bsd/net/if_headless.c - third_party/xnu/bsd/net/if_ipsec.c - third_party/xnu/bsd/net/if_ports_used.c - third_party/xnu/bsd/net/if_stf.c - third_party/xnu/bsd/net/if_vlan.c - third_party/xnu/bsd/net/init.c - third_party/xnu/bsd/net/kpi_interface.c - third_party/xnu/bsd/net/kpi_interfacefilter.c - third_party/xnu/bsd/net/kpi_protocol.c - third_party/xnu/bsd/net/linkaddr.c - third_party/xnu/bsd/net/multicast_list.c - third_party/xnu/bsd/net/ndrv.c - third_party/xnu/bsd/net/net_str_id.c - third_party/xnu/bsd/net/pf.c - third_party/xnu/bsd/net/pf_if.c - third_party/xnu/bsd/net/pf_ioctl.c - third_party/xnu/bsd/net/pf_norm.c - third_party/xnu/bsd/net/pf_osfp.c - third_party/xnu/bsd/net/pf_pbuf.c - third_party/xnu/bsd/net/pf_ruleset.c - third_party/xnu/bsd/net/pf_table.c - third_party/xnu/bsd/net/sixxlowpan.c - third_party/xnu/bsd/net/necp.c - third_party/xnu/bsd/net/necp_client.c - third_party/xnu/bsd/net/net_perf.c - third_party/xnu/bsd/net/network_agent.c - third_party/xnu/bsd/net/ntstat.c - third_party/xnu/bsd/net/pktap.c - third_party/xnu/bsd/net/pktsched/pktsched.c - third_party/xnu/bsd/net/pktsched/pktsched_fq_codel.c - third_party/xnu/bsd/net/pktsched/pktsched_netem.c - third_party/xnu/bsd/net/raw_cb.c - third_party/xnu/bsd/net/raw_usrreq.c - third_party/xnu/bsd/net/radix.c - third_party/xnu/bsd/net/route.c - third_party/xnu/bsd/net/rtsock.c - third_party/xnu/bsd/netinet/flow_divert.c - third_party/xnu/bsd/netinet/in.c - third_party/xnu/bsd/netinet/igmp.c - third_party/xnu/bsd/netinet/in_arp.c - third_party/xnu/bsd/netinet/in_cksum.c - third_party/xnu/bsd/netinet/in_mcast.c - third_party/xnu/bsd/netinet/in_pcb.c - third_party/xnu/bsd/netinet/in_pcblist.c - third_party/xnu/bsd/netinet/in_proto.c - third_party/xnu/bsd/netinet/in_rmx.c - third_party/xnu/bsd/netinet/in_tclass.c - third_party/xnu/bsd/netinet/ip_dummynet.c - third_party/xnu/bsd/netinet/ip_ecn.c - third_party/xnu/bsd/netinet/ip_encap.c - third_party/xnu/bsd/netinet/ip_icmp.c - third_party/xnu/bsd/netinet/ip_id.c - third_party/xnu/bsd/netinet/ip_input.c - third_party/xnu/bsd/netinet/ip_output.c - third_party/xnu/bsd/netinet/kpi_ipfilter.c - third_party/xnu/bsd/netinet/mp_pcb.c - third_party/xnu/bsd/netinet/mp_pcb.h - third_party/xnu/bsd/netinet/mp_proto.c - third_party/xnu/bsd/netinet/mptcp.c - third_party/xnu/bsd/netinet/mptcp.h - third_party/xnu/bsd/netinet/mptcp_opt.c - third_party/xnu/bsd/netinet/mptcp_opt.h - third_party/xnu/bsd/netinet/mptcp_subr.c - third_party/xnu/bsd/netinet/mptcp_timer.c - third_party/xnu/bsd/netinet/mptcp_timer.h - third_party/xnu/bsd/netinet/mptcp_usrreq.c - third_party/xnu/bsd/netinet/raw_ip.c - third_party/xnu/bsd/netinet/tcp_cache.c - third_party/xnu/bsd/netinet/tcp_cc.c - third_party/xnu/bsd/netinet/tcp_cubic.c - third_party/xnu/bsd/netinet/tcp_input.c - third_party/xnu/bsd/netinet/tcp_ledbat.c - third_party/xnu/bsd/netinet/tcp_log.c - third_party/xnu/bsd/netinet/tcp_newreno.c - third_party/xnu/bsd/netinet/tcp_output.c - third_party/xnu/bsd/netinet/tcp_sack.c - third_party/xnu/bsd/netinet/tcp_subr.c - third_party/xnu/bsd/netinet/tcp_timer.c - third_party/xnu/bsd/netinet/tcp_usrreq.c - third_party/xnu/bsd/netinet/udp_usrreq.c - third_party/xnu/bsd/netinet6/ah_core.c - third_party/xnu/bsd/netinet6/ah_input.c - third_party/xnu/bsd/netinet6/ah_output.c - third_party/xnu/bsd/netinet6/dest6.c - third_party/xnu/bsd/netinet6/esp_chachapoly.c - third_party/xnu/bsd/netinet6/esp_core.c - third_party/xnu/bsd/netinet6/esp_input.c - third_party/xnu/bsd/netinet6/esp_output.c - third_party/xnu/bsd/netinet6/esp_rijndael.c - third_party/xnu/bsd/netinet6/frag6.c - third_party/xnu/bsd/netinet6/icmp6.c - third_party/xnu/bsd/netinet6/in6.c - third_party/xnu/bsd/netinet6/in6_cga.c - third_party/xnu/bsd/netinet6/in6_cksum.c - third_party/xnu/bsd/netinet6/in6_ifattach.c - third_party/xnu/bsd/netinet6/in6_mcast.c - third_party/xnu/bsd/netinet6/in6_pcb.c - third_party/xnu/bsd/netinet6/in6_proto.c - third_party/xnu/bsd/netinet6/in6_rmx.c - third_party/xnu/bsd/netinet6/in6_src.c - third_party/xnu/bsd/netinet6/ipsec.c - third_party/xnu/bsd/netinet6/ip6_forward.c - third_party/xnu/bsd/netinet6/ip6_input.c - third_party/xnu/bsd/netinet6/ip6_id.c - third_party/xnu/bsd/netinet6/ip6_output.c - third_party/xnu/bsd/netinet6/mld6.c - third_party/xnu/bsd/netinet6/nd6.c - third_party/xnu/bsd/netinet6/nd6_nbr.c - third_party/xnu/bsd/netinet6/nd6_prproxy.c - third_party/xnu/bsd/netinet6/nd6_rti.c - third_party/xnu/bsd/netinet6/nd6_rtr.c - third_party/xnu/bsd/netinet6/nd6_send.c - third_party/xnu/bsd/netinet6/raw_ip6.c - third_party/xnu/bsd/netinet6/route6.c - third_party/xnu/bsd/netinet6/scope6.c - third_party/xnu/bsd/netinet6/udp6_output.c - third_party/xnu/bsd/netinet6/udp6_usrreq.c - third_party/xnu/bsd/netkey/key.c - third_party/xnu/bsd/netkey/keydb.c - third_party/xnu/bsd/netkey/keysock.c - third_party/xnu/bsd/sys/mcache.h - third_party/xnu/libkern/crypto/corecrypto_chacha20poly1305.c - third_party/xnu/libkern/crypto/corecrypto_des.c - third_party/xnu/libkern/crypto/corecrypto_sha2.c - third_party/xnu/libkern/gen/OSAtomicOperations.c - third_party/xnu/libkern/os/refcnt.c - third_party/xnu/security/mac_system.c - fuzz/api/syscall_wrappers.c - fuzz/api/ioctl.c - fuzz/api/backend.c - fuzz/fakes/stubs.c - fuzz/fakes/thread.c - fuzz/fakes/fake_impls.c - fuzz/fakes/mbuf.c -) - -set(XNU_INCLUDES - . - third_party/xnu - third_party/xnu/BUILD/obj/EXPORT_HDRS/bsd - third_party/xnu/BUILD/obj/EXPORT_HDRS/osfmk - third_party/xnu/BUILD/obj/EXPORT_HDRS/libkern - third_party/xnu/BUILD/obj/EXPORT_HDRS/iokit - third_party/xnu/BUILD/obj/EXPORT_HDRS/pexpert - third_party/xnu/BUILD/obj/EXPORT_HDRS/libsa - third_party/xnu/BUILD/obj/EXPORT_HDRS/security - third_party/xnu/BUILD/obj/EXPORT_HDRS/san - third_party/xnu/EXTERNAL_HEADERS -) - -# If SANITIZER flag is passed, we are building for OSS-Fuzz. This means we -# already pass the build CFLAGs/LDFLAGS it wants via the environment variables. -if(DEFINED ENV{SANITIZER}) - if($ENV{SANITIZER} MATCHES "address") - set(FUZZER_SOURCES fuzz/fakes/san.c - third_party/xnu/san/kasan-memintrinsics.c - ) - set(FUZZER_DEFINES -DKASAN=1) - endif() -else() - set(FUZZER_CXX_FLAGS -fsanitize=address,fuzzer-no-link) - set(FUZZER_LD_FLAGS -fsanitize=address,fuzzer) - set(ENV{LIB_FUZZING_ENGINE} -fsanitize=fuzzer) - set(FUZZER_SOURCES fuzz/fakes/san.c third_party/xnu/san/kasan-memintrinsics.c) - set(FUZZER_DEFINES -DKASAN=1) -endif() - -# These must be built separately due to header incompatabilities -set(OSFMK_SOURCES - fuzz/fakes/zalloc.c - fuzz/fakes/osfmk_stubs.c - third_party/xnu/osfmk/kern/startup.c -) - -set(BSD_INCLUDES - ${XNU_INCLUDES} third_party/xnu/BUILD/obj/DEBUG_X86_64/bsd/DEBUG - third_party/xnu/bsd third_party/xnu/san -) - -set(OSFMK_INCLUDES - ${XNU_INCLUDES} third_party/xnu/BUILD/obj/DEBUG_X86_64/osfmk/DEBUG - third_party/xnu/osfmk third_party/xnu/osfmk/libsa -) - -# We use an object library so we can include the osfmk objects which require -# different build flags into the libxnu library -add_library(osfmk OBJECT ${OSFMK_SOURCES}) -target_include_directories(osfmk PRIVATE ${OSFMK_INCLUDES}) -set_property(TARGET osfmk PROPERTY C_VISIBILITY_PRESET hidden) -set_property(TARGET osfmk PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_options( - osfmk PRIVATE ${XNU_C_FLAGS} ${OSFMK_DEFINES} ${FUZZER_DEFINES} - ${FUZZER_CXX_FLAGS} -DLIBXNU_BUILD=1 -) - -add_library(bsd OBJECT ${BSD_SOURCES} ${FUZZER_SOURCES}) -set_property(TARGET bsd PROPERTY C_VISIBILITY_PRESET hidden) -set_property(TARGET bsd PROPERTY POSITION_INDEPENDENT_CODE ON) - -add_custom_command( - OUTPUT libxnu_relocatable.o - COMMAND ld.lld -r $ $ -o libxnu_relocatable.o && objcopy --localize-hidden libxnu_relocatable.o - DEPENDS $ $ - COMMAND_EXPAND_LISTS -) - -# TODO(nedwill): fix mac build -if(APPLE) - # Dynamic lookups are needed for us to link correctly when we have mutually - # dependent targets (net_fuzzer and xnu) - target_link_options(xnu PRIVATE -Bsymbolic -undefined dynamic_lookup) -endif(APPLE) - -target_include_directories(bsd PRIVATE ${BSD_INCLUDES}) -target_compile_options( - bsd PRIVATE ${XNU_C_FLAGS} ${BSD_DEFINES} ${SAN_DEFINES} -DLIBXNU_BUILD=1 - ${FUZZER_DEFINES} ${FUZZER_CXX_FLAGS} -) - -# Only build coverage builds locally on Linux. -if(NOT DEFINED ENV{SANITIZER} AND NOT APPLE) - add_library(osfmk_cov OBJECT ${OSFMK_SOURCES}) - target_include_directories(osfmk_cov PRIVATE ${OSFMK_INCLUDES}) - set_target_properties(osfmk_cov PROPERTIES C_VISIBILITY_PRESET hidden) - set_property(TARGET osfmk_cov PROPERTY POSITION_INDEPENDENT_CODE ON) - target_compile_options( - osfmk_cov PRIVATE ${XNU_C_FLAGS} ${OSFMK_DEFINES} -DLIBXNU_BUILD=1 - -fprofile-instr-generate -fcoverage-mapping - ) - - add_library(bsd_cov STATIC ${BSD_SOURCES} $) - target_include_directories(bsd_cov PRIVATE ${BSD_INCLUDES}) - set_target_properties(bsd_cov PROPERTIES C_VISIBILITY_PRESET hidden) - target_compile_options( - bsd_cov - PRIVATE ${XNU_C_FLAGS} ${BSD_DEFINES} ${SAN_DEFINES} -DLIBXNU_BUILD=1 - -fprofile-instr-generate -fcoverage-mapping - ) -endif() - -# TODO(nedwill): get the local protobuf build working to support msan -# add_subdirectory(third_party/libprotobuf-mutator) -find_package(Protobuf REQUIRED) -include_directories(${PROTOBUF_INCLUDE_DIRS}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -# TODO(nedwill): Import CMakeLists.txt from libprotobuf-mutator folder -add_library( - protobuf-mutator STATIC - third_party/libprotobuf-mutator/src/binary_format.cc - third_party/libprotobuf-mutator/src/mutator.cc - third_party/libprotobuf-mutator/src/text_format.cc - third_party/libprotobuf-mutator/src/utf8_fix.cc - third_party/libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.cc - third_party/libprotobuf-mutator/src/libfuzzer/libfuzzer_mutator.cc - third_party/libprotobuf-mutator/src/mutator.h -) - -target_include_directories( - protobuf-mutator PRIVATE third_party/libprotobuf-mutator -) -target_compile_options(protobuf-mutator PRIVATE -std=c++11) - -if(APPLE) - target_link_libraries(protobuf-mutator) -else() - target_link_libraries(protobuf-mutator ${Protobuf_LIBRARIES}) -endif(APPLE) - -add_library(libbsd STATIC - third_party/libbsd/src/strlcat.c - third_party/libbsd/src/strlcpy.c) - -protobuf_generate_cpp(NET_PROTO_SRCS NET_PROTO_HDRS fuzz/net_fuzzer.proto) -add_executable( - net_fuzzer fuzz/net_fuzzer.cc ${NET_PROTO_SRCS} ${NET_PROTO_HDRS} libxnu_relocatable.o -) -add_dependencies(net_fuzzer bsd) - -target_include_directories(net_fuzzer PRIVATE third_party/libprotobuf-mutator) -target_compile_options( - net_fuzzer PRIVATE -g -std=c++11 -Werror -Wno-address-of-packed-member - ${FUZZER_CXX_FLAGS} -) - -if(APPLE) - target_link_libraries( - net_fuzzer ${FUZZER_LD_FLAGS} $ENV{LIB_FUZZING_ENGINE} protobuf-mutator - ${Protobuf_LIBRARIES} - ) -else() - target_link_libraries( - net_fuzzer - ${FUZZER_LD_FLAGS} - $ENV{LIB_FUZZING_ENGINE} - protobuf-mutator - ${Protobuf_LIBRARIES} - pthread - libbsd - ) -endif(APPLE) - -# if(APPLE) add_executable(net_real fuzz/net_fuzzer.cc ${NET_PROTO_SRCS} -# ${NET_PROTO_HDRS}) target_include_directories(net_real PRIVATE -# libprotobuf-mutator) target_compile_options(net_real PRIVATE -g -std=c++11 -# -Wno-address-of-packed-member -Werror) # Notice we don't include libxnu here -# since we use the real kernel. target_link_libraries(net_real fuzzer -# protobuf-mutator ${Protobuf_LIBRARIES}) endif(APPLE) - -# Only build coverage builds locally. -if(DEFINED ENV{SANITIZER}) - -else() - if(NOT APPLE) - add_custom_command( - OUTPUT libxnu_cov_relocatable.o - COMMAND ld.lld -r $ $ -o libxnu_cov_relocatable.o && objcopy --localize-hidden libxnu_cov_relocatable.o - DEPENDS $ $ - COMMAND_EXPAND_LISTS - ) - - add_executable( - net_cov fuzz/net_fuzzer.cc ${NET_PROTO_SRCS} ${NET_PROTO_HDRS} libxnu_cov_relocatable.o - ) - add_dependencies(net_cov bsd_cov) - target_include_directories(net_cov PRIVATE third_party/libprotobuf-mutator) - target_compile_options( - net_cov PRIVATE -g -std=c++11 -Werror -Wno-address-of-packed-member - -fprofile-instr-generate -fcoverage-mapping - ) - target_link_libraries( - net_cov - -fsanitize=fuzzer - -fprofile-instr-generate - -fcoverage-mapping - protobuf-mutator - ${Protobuf_LIBRARIES} - libbsd - pthread - -Wl,--exclude-libs,ALL - ) - endif(NOT APPLE) -endif() diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 9ebcb32..0000000 --- a/Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2022 Google LLC -# -# 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. - -# This Dockerfile prepares a simple Debian build environment. - -# docker build --pull -t builder . -# docker run -t -i -v $PWD:/source builder /bin/bash -FROM gcr.io/oss-fuzz-base/base-builder - -RUN apt-get update && apt-get install -y \ - autoconf \ - cmake \ - git \ - g++-multilib \ - libtool \ - ninja-build \ - python - -# Install Protobuf for C++ as the version in the base-builder repos may -# be outdated. -RUN curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v3.18.1/protobuf-cpp-3.18.1.tar.gz -RUN tar xf protobuf-cpp-3.18.1.tar.gz -WORKDIR $SRC/protobuf-3.18.1 -# Build statically -RUN ./configure --disable-shared -RUN make -j $(nproc) -RUN make install - -WORKDIR $SRC -# You can now build using cmake. I use a subdirectory "build". -COPY build.sh $SRC diff --git a/README.md b/README.md index 507d77a..a4d4e08 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,100 @@ -# SockFuzzer +# SockFuzzer: XNU Kernel Fuzzing Framework -This repository contains SockFuzzer, a fork of XNU that contains support -for fuzzing the network stack in userland on macOS and Linux-based hosts. +## Project Status -# Building and Using the Fuzzer +SockFuzzer, originally designed as a networking-focused fuzzer for the XNU kernel (used in macOS and iOS), has evolved into a comprehensive kernel fuzzing framework. While retaining its original name as a nod to its roots, SockFuzzer now covers a wide range of kernel subsystems, including BSD, Mach, virtual memory, and more. The project implements a unique approach by converting the XNU kernel into a library that can be "booted" and fuzzed in userspace, allowing for efficient vulnerability discovery and reproduction across multiple subsystems. -NOTE: The project is moving to a Bazel-based build system. The following steps -work for now but will be updated once the new build system is published. +## Project Goals -Build the fuzzer the same way you would typically build a project using CMake -for your platform. For example: +1. Comprehensively test the XNU kernel in a controlled environment +2. Discover and reproduce vulnerabilities across various XNU subsystems +3. Provide a framework for continuous fuzzing and improvement of XNU security +4. Demonstrate the value of implementing advanced fuzzing techniques in kernel research +5. Enable efficient testing of complex kernel interactions and subsystems -``` -$ mkdir build; cd build -$ CC=clang CXX=clang++ cmake -GNinja .. -$ ninja -``` +## Architecture Overview -You can now run the `net_fuzzer` binary, optionally providing parameters as -described in the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html). +SockFuzzer employs a host/"XNU guest" design, where the XNU kernel is compiled as a library and run within a custom userspace environment. This architecture consists of several key components: -A Dockerfile is included which shows how to prepare a Debian environment to build -the project. Feel free to use this container to build and run the fuzzer. +1. Host Environment +2. XNU Guest Library +3. Custom Scheduler (based on Concurrence) +4. Fuzzing Engine (Centipede from Google's fuzztest project) +5. Test Runner and Harness -# Extending the Fuzzer +### Host Environment -This project is currently an all-in-one fuzzer for XNU networking. You can extend -it by adding additional targets to CMakeLists.txt or by extending the existing -network target. Nothing about this project specifically prevents the testing of -additional non-networking subsystems, so feel free to extend it to test other -areas. +The host environment provides the foundation for running the XNU guest library and managing the fuzzing process. It includes: -# Generating and Reviewing Coverage Reports +- Custom implementations of core kernel services +- Hypercall interface for communication between the host and XNU guest +- Test runner and harness for executing fuzz tests -Coverage reports are an important way to review the quality of the current -fuzzer implementation. On Linux, a `net_cov` binary is generated containing -[LLVM's source based code-coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) -instrumentation. +### XNU Guest Library -At the time of writing, the following commands product an HTML coverage report located -in the `report` folder after running all of the testcases located in the `corpus` folder. +The XNU kernel is compiled as a library (`libxnu`) with minimal modifications to run in userspace. Key aspects include: -``` -./net_cov corpus -llvm-profdata merge -sparse default.profraw -o default.profdata -llvm-cov show -format=html -output-dir=report -instr-profile=default.profdata net_cov -``` +- Symbol prefixing and management to avoid conflicts with host symbols +- Selective exposure of internal XNU functions through a version script +- Support for multiple subsystems: BSD, Mach, virtual memory, threads, processes, etc. +- Null pmap layer for virtual memory support (with plans to support ARM pmap in the future) -# Importing upstream XNU releases +### Custom Scheduler -A macOS environment is needed to generate the new files. Unpack the new source tarball -replacing third_party/xnu. Then run the following command, updating SDKROOT as needed. -Then you can add BUILD/obj/EXPORT_HDRS and BUILD/obj/DEBUG_X86_64 to the git repo. -You'll also need to rebase any changes to the original XNU sources. In some cases, the -outer CMakeLists.txt must also be updated to reflect new or deleted source paths. +Based on the improved Concurrence project, the custom scheduler now supports full threading capabilities: -I use an upstream branch to facilitate merging my patches with the upstream changes. +- Executor: Provides thread creation, deletion, and context switching +- FuzzedScheduler: Manages thread states and scheduling decisions +- Integration with the XNU guest library for proper multithreading support -``` -# From inside third_party/xnu -$ make SDKROOT=macosx11.1 ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEBUG -$ git add BUILD/obj/EXPORT_HDRS EXTERNAL_HEADERS -``` +### Test Runner and Harness -# Disclaimer +The test runner and harness manage the execution of fuzz tests and provide: -This is not an official Google product. +- Initialization of the XNU guest environment +- Execution of fuzz inputs across various subsystems +- Crash detection and reporting + +## Key Features + +1. Comprehensive XNU Kernel Coverage: Test multiple subsystems including BSD, Mach, virtual memory, threads, and processes +2. Userspace XNU Execution: Run XNU kernel components in a controlled userspace environment +3. Full Threading Support: Leverage improved Concurrence for proper multithreading capabilities +4. Virtual Memory Support: Utilize a null pmap layer with plans for ARM pmap support +5. MIG Fuzzing: Support for fuzzing Mach Interface Generator (MIG) interfaces +6. Hypercall Interface: Facilitate communication between host and XNU guest components + +## Supported Features + +1. BSD +2. Mach +3. Virtual Memory (with null pmap layer) +4. Threads and Processes +5. Networking +6. Mach Messages +7. MIG Interfaces + +## Workflow + +1. The XNU kernel is compiled as a library with necessary modifications +2. The host environment initializes the XNU guest library and custom scheduler +3. Centipede generates structured inputs using protobuf definitions +4. The test harness executes the inputs, invoking syscalls, Mach messages, and other kernel interfaces +5. The custom scheduler manages thread execution within the XNU guest +6. ASAN monitors for memory corruption issues +7. Crashes and coverage information are collected and analyzed + +## Limitations and Future Work + +1. IOKit Support: The current implementation does not support IOKit subsystems +2. Binary-only Fuzzing: The approach currently relies on source code which doesn't include all modules + +Future work includes: +- Implementing IOKit support +- Integrating ARM pmap support for virtual memory +- Adapting techniques for binary-only fuzzing scenarios +- Continuing to expand coverage and support for XNU subsystems + +## Public Release Limitations + +In order to keep this repository well-factored, several dependencies will need to be added by users themselves to third_party, such as xnu, bootstrap_cmds, and a few other libraries. \ No newline at end of file diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel new file mode 100644 index 0000000..2f44def --- /dev/null +++ b/WORKSPACE.bazel @@ -0,0 +1,633 @@ +workspace(name = "sockfuzzer") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +SKYLIB_VERSION = "1.4.1" + +http_archive( + name = "bazel_skylib", + sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/{version}/bazel-skylib-{version}.tar.gz".format(version = SKYLIB_VERSION), + "https://github.com/bazelbuild/bazel-skylib/releases/download/{version}/bazel-skylib-{version}.tar.gz".format(version = SKYLIB_VERSION), + ], +) + +http_archive( + name = "rules_foreign_cc", + sha256 = "476303bd0f1b04cc311fc258f1708a5f6ef82d3091e53fd1977fa20383425a6a", + strip_prefix = "rules_foreign_cc-0.10.1", + url = "https://github.com/bazelbuild/rules_foreign_cc/releases/download/0.10.1/rules_foreign_cc-0.10.1.tar.gz", +) + +load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies") + +rules_foreign_cc_dependencies() + +http_archive( + name = "rules_cc", + sha256 = "d75a040c32954da0d308d3f2ea2ba735490f49b3a7aa3e4b40259ca4b814f825", + urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.0.10-rc1/rules_cc-0.0.10-rc1.tar.gz"], +) + +# We use nixpkgs to help us reduce the amount of projects we need to support with the Bazel rules. +# It may be better to migrate to something lower level like `rules_foreign_cc` in the future, but +# this current solution despite its complexity and nix dependency brings maintained and up-to-date +# packages to our project. +http_archive( + name = "io_tweag_rules_nixpkgs", + sha256 = "f99b8832499d508d9c519d219636a438252d1c5a43dbb655fa2231fd995a85d8", + strip_prefix = "rules_nixpkgs-b6cf1734d4c52dba33f942d6b5cd5538dfa6e0b6", + url = "https://github.com/tweag/rules_nixpkgs/archive/b6cf1734d4c52dba33f942d6b5cd5538dfa6e0b6.tar.gz", +) + +# load everything that rules_nixpkgs rules need to work +load("@io_tweag_rules_nixpkgs//nixpkgs:repositories.bzl", "rules_nixpkgs_dependencies") + +rules_nixpkgs_dependencies() + +load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_cc_configure", "nixpkgs_local_repository", "nixpkgs_package") + +nixpkgs_local_repository( + name = "nixpkgs", + nix_file = "//:nixpkgs.nix", + nix_file_deps = ["//:flake.lock"], +) + +nixpkgs_cc_configure( + name = "llvm_toolchain", + nix_file = "//:llvm_toolchain.nix", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "elfutils_headers", + attribute_path = "elfutils.dev", + build_file_content = """ +filegroup( + name = "include", + srcs = glob(["include/**/*.h"]), + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "elfutils", + attribute_path = "elfutils.out", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "dw", + srcs = glob(["lib/**/*libdw*.so*"]), + hdrs = ["@elfutils_headers//:include"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "lzma_headers", + attribute_path = "xz.dev", + build_file_content = """ +filegroup( + name = "include", + srcs = glob(["include/**/*.h"]), + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "lzma", + attribute_path = "xz.out", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "lzma", + srcs = glob(["lib/**/*liblzma*.so*"]), + hdrs = ["@lzma_headers//:include"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "libunwind_headers", + attribute_path = "libunwind.dev", + build_file_content = """ +filegroup( + name = "include", + srcs = glob(["include/**/*.h"]), + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +# TODO(nedwill): consider pkgsStatic versions of these once we have a way +# to ensure symbols don't collide with the fuzz binary +nixpkgs_package( + name = "libunwind", + attribute_path = "libunwind", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +filegroup( + name = "lib", + srcs = glob(["lib/**/*libunwind*.so*"]), + visibility = ["//visibility:public"], +) + +cc_library( + name = "libunwind", + srcs = [":lib"], + hdrs = ["@libunwind_headers//:include"], + deps = ["@lzma//:lzma"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "compiler_rt_dev", + attribute_path = "llvmPackages_18.compiler-rt.dev", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +filegroup( + name = "include", + srcs = glob(["include/**/*.h"]), + visibility = ["//visibility:public"], +) + +cc_library( + name = "compiler-rt-headers", + hdrs = [":include"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +# .dev has headers, .lib has libraries + +nixpkgs_package( + name = "llvm_headers", + attribute_path = "llvmPackages_18.llvm.dev", + build_file_content = """ +filegroup( + name = "include", + srcs = glob(["include/**/*.h"]), + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "llvm", + attribute_path = "llvmPackages_18.llvm.lib", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "libllvm", + srcs = glob(["lib/lib*.so*"]), + hdrs = ["@llvm_headers//:include"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "clang_headers", + attribute_path = "llvmPackages_18.clang-unwrapped.dev", + build_file_content = """ +filegroup( + name = "include", + srcs = glob(["include/**/*.h", "include/**/*.def"]), + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +# Clang libraries +nixpkgs_package( + name = "clang", + attribute_path = "llvmPackages_18.clang-unwrapped.lib", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "libclang", + srcs = glob(["lib/lib*.so*"]), + hdrs = ["@clang_headers//:include"], + deps = ["@llvm//:libllvm"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "openssl_headers", + attribute_path = "openssl.dev", + build_file_content = """ +filegroup( + name = "include", + srcs = glob(["include/**/*.h"]), + visibility = ["//visibility:public"], +) +""", + repository = "@nixpkgs", +) + +nixpkgs_package( + name = "openssl", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "crypto", + srcs = glob(["lib/**/*libcrypto*.a*"]), + hdrs = ["@openssl_headers//:include"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""", + nix_file_content = """ +{ pkgs ? import {} }: + +let + openssl = pkgs.openssl.override { static = true; }; + # Directly select the 'out' output path + opensslPath = openssl.out; +in + pkgs.stdenv.mkDerivation rec { + name = "openssl"; + src = opensslPath; + + installPhase = '' + mkdir -p $out/lib + cp -r $src/lib/* $out/lib/ + ''; + } +""", + repository = "@nixpkgs", +) + +http_archive( + name = "rules_python", + sha256 = "778aaeab3e6cfd56d681c89f5c10d7ad6bf8d2f1a72de9de55b23081b2d31618", + strip_prefix = "rules_python-0.34.0", + url = "https://github.com/bazelbuild/rules_python/releases/download/0.34.0/rules_python-0.34.0.tar.gz", +) + +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") + +py_repositories() + +python_register_toolchains( + name = "python_3_10", + python_version = "3.10", +) + +load("@python_3_10//:defs.bzl", "interpreter") +load("@rules_python//python:pip.bzl", "pip_parse") + +pip_parse( + name = "tools_deps", + python_interpreter_target = interpreter, + requirements_lock = "//:requirements-lock.txt", +) + +local_repository( + name = "com_google_absl", + path = "third_party/abseil-cpp", +) + +http_archive( + name = "com_google_riegeli", + sha256 = "f8386e44e16d044c1d7151c0b553bb7075d79583d4fa9e613a4be452599e0795", + strip_prefix = "riegeli-411cda7f6aa81f8b8591b04cf141b1decdcc928c", + url = "https://github.com/google/riegeli/archive/411cda7f6aa81f8b8591b04cf141b1decdcc928c.tar.gz", +) + +http_archive( + name = "highwayhash", + build_file = "@com_google_riegeli//third_party:highwayhash.BUILD", + sha256 = "cf891e024699c82aabce528a024adbe16e529f2b4e57f954455e0bf53efae585", + strip_prefix = "highwayhash-276dd7b4b6d330e4734b756e97ccfb1b69cc2e12", + urls = ["https://github.com/google/highwayhash/archive/276dd7b4b6d330e4734b756e97ccfb1b69cc2e12.zip"], # 2019-02-22 +) + +http_archive( + name = "org_brotli", + sha256 = "84a9a68ada813a59db94d83ea10c54155f1d34399baf377842ff3ab9b3b3256e", + strip_prefix = "brotli-3914999fcc1fda92e750ef9190aa6db9bf7bdb07", + urls = ["https://github.com/google/brotli/archive/3914999fcc1fda92e750ef9190aa6db9bf7bdb07.zip"], # 2022-11-17 +) + +http_archive( + name = "net_zstd", + build_file = "@com_google_riegeli//third_party:net_zstd.BUILD", + sha256 = "b6c537b53356a3af3ca3e621457751fa9a6ba96daf3aebb3526ae0f610863532", + strip_prefix = "zstd-1.4.5/lib", + urls = ["https://github.com/facebook/zstd/archive/v1.4.5.zip"], # 2020-05-22 +) + +http_archive( + name = "snappy", + build_file = "@com_google_riegeli//third_party:snappy.BUILD", + sha256 = "38b4aabf88eb480131ed45bfb89c19ca3e2a62daeb081bdf001cfb17ec4cd303", + strip_prefix = "snappy-1.1.8", + urls = ["https://github.com/google/snappy/archive/1.1.8.zip"], # 2020-01-14 +) + +http_archive( + name = "rules_pkg", + sha256 = "8a298e832762eda1830597d64fe7db58178aa84cd5926d76d5b744d6558941c2", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + ], +) + +load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") + +rules_pkg_dependencies() + +http_archive( + name = "com_google_protobuf", + sha256 = "b07772d38ab07e55eca4d50f4b53da2d998bb221575c60a4f81100242d4b4889", + strip_prefix = "protobuf-3.20.0", + urls = [ + "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.20.0.tar.gz", + ], +) + +http_archive( + name = "rules_m4", + sha256 = "b0309baacfd1b736ed82dc2bb27b0ec38455a31a3d5d20f8d05e831ebeef1a8e", + urls = ["https://github.com/jmillikin/rules_m4/releases/download/v0.2.2/rules_m4-v0.2.2.tar.xz"], +) + +load("@rules_m4//m4:m4.bzl", "m4_register_toolchains") + +m4_register_toolchains() + +http_archive( + name = "rules_bison", + sha256 = "9577455967bfcf52f9167274063ebb74696cb0fd576e4226e14ed23c5d67a693", + urls = ["https://github.com/jmillikin/rules_bison/releases/download/v0.2.1/rules_bison-v0.2.1.tar.xz"], +) + +load("@rules_bison//bison:bison.bzl", "bison_register_toolchains") + +bison_register_toolchains() + +http_archive( + name = "rules_flex", + sha256 = "2b11316dfae3292999c6acc99c0bdfdc6bdc25bb32b1e07c0840529e255e5e0f", + strip_prefix = "rules_flex-c40bf6a62dfcd4e849eef9bf1489f75ee987944e", + urls = ["https://github.com/jmillikin/rules_flex/archive/c40bf6a62dfcd4e849eef9bf1489f75ee987944e.zip"], +) + +load("@rules_flex//flex:flex.bzl", "flex_register_toolchains") + +flex_register_toolchains() + +http_archive( + name = "com_googlesource_code_re2", + sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9", + strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502", + urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"], # 2022-04-08 +) + +http_archive( + name = "com_google_googletest", + sha256 = "7ee83802222f9392452c57b4757185697a51639b69b64590f2c2188f58618581", + strip_prefix = "googletest-8d51dc50eb7e7698427fed81b85edad0e032112e", + urls = ["https://github.com/google/googletest/archive/8d51dc50eb7e7698427fed81b85edad0e032112e.zip"], +) + +http_archive( + name = "rules_proto", + sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd", + strip_prefix = "rules_proto-5.3.0-21.7", + urls = [ + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz", + ], +) + +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") + +rules_proto_dependencies() + +rules_proto_toolchains() + +http_archive( + name = "ios_sdk", + build_file = "//:bazel/BUILD.sdk", + sha256 = "35d6cc8d694d42db639c32c451ef5fcfb4679daf2c3c5bb295cc7391e317128e", + strip_prefix = "15.4", + url = "https://github.com/filsv/iPhoneOSDeviceSupport/raw/master/15.4.zip", +) + +# Hedron's Compile Commands Extractor for Bazel +# https://github.com/hedronvision/bazel-compile-commands-extractor +http_archive( + name = "hedron_compile_commands", + sha256 = "044b148b111e17cee61d8c3aaeed42069f7325460382340afca4919f8265094c", + strip_prefix = "bazel-compile-commands-extractor-1e08f8e0507b6b6b1f4416a9a22cf5c28beaba93", + url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/1e08f8e0507b6b6b1f4416a9a22cf5c28beaba93.tar.gz", +) + +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") + +hedron_compile_commands_setup() + +http_archive( + name = "rules_proto_grpc", + sha256 = "507e38c8d95c7efa4f3b1c0595a8e8f139c885cb41a76cab7e20e4e67ae87731", + strip_prefix = "rules_proto_grpc-4.1.1", + urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/4.1.1.tar.gz"], +) + +load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains") + +rules_proto_grpc_toolchains() + +load("@rules_proto_grpc//python:repositories.bzl", rules_proto_grpc_python_repos = "python_repos") + +rules_proto_grpc_python_repos() + +# Import "install_deps" as "install_tools_deps" to avoid name collision with +# the "install_deps" function in the "rules_python" repository. +load("@tools_deps//:requirements.bzl", install_tools_deps = "install_deps") + +install_tools_deps() + +local_repository( + name = "com_google_fuzztest", + path = "third_party/fuzztest", +) + +http_archive( + name = "aspect_rules_js", + sha256 = "08061ba5e5e7f4b1074538323576dac819f9337a0c7d75aee43afc8ae7cb6e18", + strip_prefix = "rules_js-1.26.1", + url = "https://github.com/aspect-build/rules_js/releases/download/v1.26.1/rules_js-v1.26.1.tar.gz", +) + +http_archive( + name = "aspect_rules_jest", + sha256 = "d3bb833f74b8ad054e6bff5e41606ff10a62880cc99e4d480f4bdfa70add1ba7", + strip_prefix = "rules_jest-0.18.4", + url = "https://github.com/aspect-build/rules_jest/releases/download/v0.18.4/rules_jest-v0.18.4.tar.gz", +) + +load("@aspect_rules_jest//jest:dependencies.bzl", "rules_jest_dependencies") + +rules_jest_dependencies() + +http_archive( + name = "aspect_rules_ts", + sha256 = "ace5b609603d9b5b875d56c9c07182357c4ee495030f40dcefb10d443ba8c208", + strip_prefix = "rules_ts-1.4.0", + url = "https://github.com/aspect-build/rules_ts/releases/download/v1.4.0/rules_ts-v1.4.0.tar.gz", +) + +load("@aspect_rules_ts//ts:repositories.bzl", "rules_ts_dependencies") + +rules_ts_dependencies( + ts_version_from = "//tools/group_crashes/frontend:package.json", +) + +load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies") + +rules_js_dependencies() + +load("@rules_nodejs//nodejs:repositories.bzl", "DEFAULT_NODE_VERSION", "nodejs_register_toolchains") + +nodejs_register_toolchains( + name = "nodejs", + node_version = DEFAULT_NODE_VERSION, +) + +load("@aspect_rules_js//npm:repositories.bzl", "npm_translate_lock") + +npm_translate_lock( + name = "npm_sockfuzzer", + bins = { + # derived from "bin" attribute in node_modules/react-scripts/package.json + "react-scripts": { + "react-scripts": "./bin/react-scripts.js", + }, + }, + pnpm_lock = "//tools/group_crashes/frontend:pnpm-lock.yaml", + public_hoist_packages = { + "eslint-config-react-app": [""], + "eslint": [""], + }, + verify_node_modules_ignored = "//:.bazelignore", +) + +load("@npm_sockfuzzer//:repositories.bzl", "npm_repositories") + +npm_repositories() + +http_archive( + name = "io_bazel_rules_docker", + sha256 = "c27b53d53a5704fb676078843f1a674ff196ab4fb9d7f6b74cf7748b47c9374f", + strip_prefix = "rules_docker-8e70c6bcb584a15a8fd061ea489b933c0ff344ca", + url = "https://github.com/bazelbuild/rules_docker/archive/8e70c6bcb584a15a8fd061ea489b933c0ff344ca.tar.gz", +) + +load( + "@io_bazel_rules_docker//repositories:repositories.bzl", + container_repositories = "repositories", +) + +container_repositories() + +load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps") + +container_deps(go_repository_default_config = "@//:WORKSPACE.bazel") + +load( + "@io_bazel_rules_docker//container:container.bzl", + "container_load", +) + +load("@io_bazel_rules_docker//repositories:py_repositories.bzl", "py_deps") + +py_deps() + +load( + "@io_bazel_rules_docker//python:image.bzl", + py_image_repos = "repositories", +) + +py_image_repos() + +load( + "@io_bazel_rules_docker//python3:image.bzl", + py3_image_repos = "repositories", +) + +py3_image_repos() + +nixpkgs_package( + name = "raw_sockfuzzer_base_image", + build_file_content = """ +package(default_visibility = [ "//visibility:public" ]) +exports_files(["image.tar"]) + """, + nix_file = "//:sockfuzzer_base_image.nix", + repository = "@nixpkgs//:nixpkgs.nix", +) + +container_load( + name = "sockfuzzer_base_image", + file = "@raw_sockfuzzer_base_image//:image.tar", +) + +http_archive( + name = "cmocka", + build_file_content = """ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") + +cmake( + name = "cmocka", + lib_source = ".", + working_directory = "cmocka", + generate_args = [ + "-DCMAKE_BUILD_TYPE=Debug", + "-DBUILD_SHARED_LIBS=OFF", + "-DWITH_EXAMPLES=OFF", + ], + out_static_libs = ["libcmocka.a"], + visibility = ["//visibility:public"], +) + """, + sha256 = "810570eb0b8d64804331f82b29ff47c790ce9cd6b163e98d47a4807047ecad82", + strip_prefix = "cmocka-1.1.7", + urls = ["https://cmocka.org/files/1.1/cmocka-1.1.7.tar.xz"], +) diff --git a/bazel/asan_dbg_binary.bzl b/bazel/asan_dbg_binary.bzl new file mode 100644 index 0000000..a8cd567 --- /dev/null +++ b/bazel/asan_dbg_binary.bzl @@ -0,0 +1,50 @@ +# Copyright 2024 Google LLC +# +# 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. + +def _asan_dbg_transition_impl(settings, _attrs): + return { + "//command_line_option:copt": settings["//command_line_option:copt"] + ['-fsanitize=address'], + "//command_line_option:linkopt": settings["//command_line_option:linkopt"] + ['-fsanitize=address'], + "//command_line_option:compilation_mode": "dbg", + "//command_line_option:platform_suffix": "asan", + } + +asan_dbg_transition = transition( + implementation = _asan_dbg_transition_impl, + inputs = [ + "//command_line_option:copt", + "//command_line_option:linkopt", + "//command_line_option:compilation_mode", + ], + outputs = [ + "//command_line_option:copt", + "//command_line_option:linkopt", + "//command_line_option:compilation_mode", + "//command_line_option:platform_suffix", + ], +) + +def _asan_cc_binary_impl(ctx): + cc_binary_target = ctx.attr.cc_binary_target[0] + return [DefaultInfo(files = cc_binary_target.default_runfiles.files)] + +asan_cc_binary = rule( + implementation = _asan_cc_binary_impl, + attrs = { + "cc_binary_target": attr.label(cfg = asan_dbg_transition, executable = True, allow_single_file = True), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist" + ), + }, +) diff --git a/bazel/cc_object.bzl b/bazel/cc_object.bzl new file mode 100644 index 0000000..8e302fb --- /dev/null +++ b/bazel/cc_object.bzl @@ -0,0 +1,84 @@ +# Copyright 2024 Google LLC +# +# 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. + +# cmd = "ld.lld -r -o $@ $(SRCS)", +# cmd = "objcopy --localize-hidden $< $@", + +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") + +def _cc_object_impl(ctx): + cc_toolchain = find_cpp_toolchain(ctx) + prelinked_object = ctx.actions.declare_file("{}_prelinked".format(ctx.label.name)) + final_object = ctx.actions.declare_file(ctx.label.name) + redefine_syms_file = ctx.file.redefine_syms + + if not redefine_syms_file: + fail("Attribute redefine_syms is required and must be a single file") + + link_args = ctx.actions.args() + + # TODO(nedwill): Consider simply redefining syms in the relevant + # dependent objects and forwarding them without actually prelinking + # all of them. This change could speed up compile times and improve link + # time optimization. + link_args.add("-r") + link_args.add("-o", prelinked_object) + + # Add deps + linker_input_paths = [] + for dep in ctx.attr.deps: + for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list(): + for library in linker_input.libraries: + linker_input_paths.extend(library.pic_objects) + link_args.add_all(linker_input_paths) + + ctx.actions.run( + mnemonic = "PartialLink", + executable = cc_toolchain.ld_executable, + arguments = [link_args], + inputs = depset( + direct = linker_input_paths, + transitive = [ + cc_toolchain.all_files, + ], + ), + outputs = [prelinked_object], + ) + + redefine_syms_args = ctx.actions.args() + redefine_syms_args.add("--redefine-syms") + redefine_syms_args.add(redefine_syms_file) + redefine_syms_args.add(prelinked_object) + redefine_syms_args.add(final_object) + ctx.actions.run( + mnemonic = "RedefineSymbols", + executable = cc_toolchain.objcopy_executable, + arguments = [redefine_syms_args], + inputs = [prelinked_object, redefine_syms_file], + outputs = [final_object], + ) + + return [DefaultInfo(files = depset([final_object]))] + +cc_object = rule( + implementation = _cc_object_impl, + attrs = { + "deps": attr.label_list(providers = [CcInfo]), + "redefine_syms": attr.label(allow_single_file = True), + "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), + }, + incompatible_use_toolchain_transition = True, + fragments = ["cpp"], + toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], +) diff --git a/bazel/generic_cc_binary.bzl b/bazel/generic_cc_binary.bzl new file mode 100644 index 0000000..ac3d797 --- /dev/null +++ b/bazel/generic_cc_binary.bzl @@ -0,0 +1,61 @@ +# Copyright 2024 Google LLC +# +# 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. + +def _generic_transition_impl(settings, attrs): + return { + "//command_line_option:copt": settings["//command_line_option:copt"] + attrs.copts, + "//command_line_option:linkopt": settings["//command_line_option:linkopt"] + attrs.linkopts, + "//command_line_option:compilation_mode": attrs.compilation_mode, + "//command_line_option:platform_suffix": attrs.platform_suffix, + } + +generic_transition = transition( + implementation = _generic_transition_impl, + inputs = [ + "//command_line_option:copt", + "//command_line_option:linkopt", + "//command_line_option:compilation_mode", + ], + outputs = [ + "//command_line_option:copt", + "//command_line_option:linkopt", + "//command_line_option:compilation_mode", + "//command_line_option:platform_suffix", + ], +) + +def _generic_cc_binary_impl(ctx): + cc_binary_target = ctx.attr.cc_binary_target[0] + output_binary = ctx.actions.declare_file(ctx.label.name) + ctx.actions.run_shell( + inputs = cc_binary_target.files.to_list(), + outputs = [output_binary], + command = "cp $1 $2", + arguments = [cc_binary_target.files.to_list()[0].path, output_binary.path], + ) + return [DefaultInfo(files = depset([output_binary]))] + +generic_cc_binary = rule( + implementation = _generic_cc_binary_impl, + attrs = { + "cc_binary_target": attr.label(cfg = generic_transition, executable = True, allow_single_file = True), + "copts": attr.string_list(default = ['-fsanitize=address']), + "linkopts": attr.string_list(default = ['-fsanitize=address']), + "compilation_mode": attr.string(default = "dbg"), + "platform_suffix": attr.string(default = "asan"), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist" + ), + }, +) diff --git a/bazel/headers.bzl b/bazel/headers.bzl new file mode 100644 index 0000000..d750e84 --- /dev/null +++ b/bazel/headers.bzl @@ -0,0 +1,41 @@ +# Copyright 2024 Google LLC +# +# 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. + +""" +Helpers for header inclusion in XNU +""" + +def get_headers(includes): + result = [] + unique_includes = {folder: True for folder in includes} + + for folder in unique_includes: + if folder == ".": + continue + + # Split the folder by "/" and check if any prefix is in the unique_includes set + folder_parts = folder.split("/") + should_skip = False + + for i in range(1, len(folder_parts)): + prefix = "/".join(folder_parts[:i]) + if prefix in unique_includes: + should_skip = True + break + + if not should_skip: + headers = native.glob([folder + "/**/*.h"]) + result.extend(headers) + + return result diff --git a/bazel/mig.bzl b/bazel/mig.bzl new file mode 100644 index 0000000..983a165 --- /dev/null +++ b/bazel/mig.bzl @@ -0,0 +1,52 @@ +# Copyright 2024 Google LLC +# +# 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. + +""" +Helpers for MIG code generation +""" + +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") + +def _preprocess_mig_defs_impl(ctx): + cc_toolchain = find_cpp_toolchain(ctx) + output_names = [] + for src in ctx.files.srcs: + base_name = src.basename.replace(".defs", "") + output_name = ctx.actions.declare_file("{}.c".format(base_name)) + output_names.append(output_name) + + cc_binary = cc_toolchain.compiler_executable + + ctx.actions.run_shell( + inputs = [src] + ctx.files.headers + ctx.files.defs, + outputs = [output_name], + command = "{} -xc -E {} -DKERNEL -DKERNEL_SERVER -I third_party/xnu/osfmk > {}".format( + cc_binary, + src.path, + output_name.path, + ), + ) + + return [DefaultInfo(files = depset(output_names))] + +preprocess_mig_defs = rule( + implementation = _preprocess_mig_defs_impl, + attrs = { + "srcs": attr.label_list(allow_files = True), + "headers": attr.label_list(allow_files = True), + "defs": attr.label_list(allow_files = True), + "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), + }, + toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], +) diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..18c55ec --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,96 @@ +# SockFuzzer XNU Guest Library Architecture + +## Overview + +The XNU Guest Library is a core component of SockFuzzer, enabling the execution of XNU kernel code in a userspace environment. This document outlines the architecture of the XNU Guest Library, including symbol management, build system, configuration, and the Hypercall Interface. + +## XNU Guest Library Architecture + +### Library Structure + +The XNU Guest Library (`libxnu`) is a compiled version of the XNU kernel source code, modified to run in userspace. It includes: + +- Core XNU kernel components +- BSD subsystem +- Mach subsystem +- Virtual memory subsystem (with null pmap layer) +- Networking stack +- Other essential kernel services + +### Symbol Management + +To avoid conflicts between XNU symbols and host symbols, SockFuzzer employs an explicit symbol management strategy: + +- All XNU symbols are prefixed with `xnu_` during the build process to ensure that internal references within the XNU kernel library resolve correctly while allowing custom code to interact with it. +- A `libxnu.redef` file maps original function names to their prefixed versions. This allows for controlled exposure of specific XNU functions to the custom code. +- An `xnu_version_script.lds` file explicitly defines which symbols are exposed from the XNU library. This includes custom XNU-side functions (e.g., `XNUThreadMarkWaitLocked`) and other necessary symbols. + +### Fake Implementations + +For kernel subsystems unrelated to the target functionality or incompatible with userspace execution, SockFuzzer provides fake implementations: + +- Simplified versions of complex kernel services +- Null implementations for non-essential functions +- Host-based implementations for core services (e.g., memory allocation) + +## Build System and Configuration + +SockFuzzer uses Bazel as its build system, which provides efficient and reproducible builds. Custom rules are used to handle the unique requirements of building the XNU kernel as a library. + +### Build Process + +1. XNU source code is compiled into individual object files +2. The `cc_object` custom rule is applied to handle symbol management +3. Object files are linked together to form `libxnu.o` +4. Symbol prefixing and redefinition are applied +5. The resulting library is linked with SockFuzzer components + +### Key Build Files + +- `BUILD.bazel`: Defines the build targets and dependencies +- `libxnu.redef`: Contains symbol redefinition mappings +- `xnu_version_script.lds`: Specifies exposed symbols + +### Custom Bazel Rules + +SockFuzzer employs custom Bazel rules to facilitate the unique build requirements: + +- `cc_object`: A custom rule for creating object files with symbol modifications + - Handles partial linking of XNU components + - Applies symbol redefinitions + - Manages symbol visibility + +Example usage of `cc_object`: + +```python +cc_object( + name = "libxnu.o", + redefine_syms = "libxnu.redef", + deps = LIBXNU_DEPS, +) +``` + +## Hypercall Interface + +The Hypercall Interface facilitates communication between the XNU Guest Library and the host environment. This enables XNU guest code to request services from the host and allows the host to control and monitor XNU guest execution. + +### Key Hypercalls + +- `ThreadPrintf`: Print debug information from XNU guest code +- `ThreadBlockReason`: Implement thread blocking in the custom scheduler +- `ThreadSetContinuation`: Set a continuation for a blocked thread +- Various thread state management calls (e.g., `ThreadSetWaiting`, `ThreadClearWait`) + +### Implementation + +- Hypercalls are implemented as function pointers in a structure +- The host environment populates this structure with appropriate implementations +- XNU guest code calls these functions through a global interface pointer + +### Usage in XNU Guest + +Example of a hypercall in XNU guest code: + +```c +GetHypercallInterface()->ThreadPrintf("Debug message from XNU guest\n"); +``` diff --git a/docs/scheduler.md b/docs/scheduler.md new file mode 100644 index 0000000..c3cced7 --- /dev/null +++ b/docs/scheduler.md @@ -0,0 +1,79 @@ +# SockFuzzer Custom Scheduler Documentation + +## Overview + +The SockFuzzer custom scheduler enables deterministic and reproducible testing of kernel components in a userspace environment by leveraging the Concurrence project. This document outlines the scheduler's architecture, key components, and its integration with the host/XNU guest design. + +## Host/XNU Guest Design + +SockFuzzer employs a host/XNU guest design to facilitate the fuzzing process: + +- Host: The userspace environment that manages the fuzzing process and provides core services. +- XNU Guest: The XNU kernel compiled as a library (`libxnu`) that runs within the host environment. + +This separation allows for fine-grained control over kernel execution and enables efficient fuzzing of XNU components. + +## Scheduler Architecture + +The custom scheduler is based on the Concurrence project and consists of two main components: + +1. Executor +2. FuzzedScheduler + +### Executor + +The Executor provides low-level thread management capabilities: + +- Thread creation and deletion +- Context switching +- Thread state management + +Key methods: +- `CreateThread(std::function target)` +- `DeleteThread(ThreadHandle handle)` +- `SwitchTo(ThreadHandle handle)` +- `GetCurrentThreadHandle()` + +### FuzzedScheduler + +The FuzzedScheduler builds upon the Executor to provide high-level scheduling decisions and thread state management: + +- Thread state transitions (runnable, blocked, etc.) +- Fuzzer-driven thread selection +- Integration with the XNU guest library + +Key methods: +- `CreateThread(std::function target, bool runnable)` +- `ChooseThread(bool can_choose_existing)` +- `MakeRunnable(ThreadHandle handle)` +- `Block()` +- `Yield()` + +## Integration with Host/XNU Guest Design + +The scheduler operates within the host environment but manages threads running XNU guest code. This integration is achieved through several mechanisms: + +1. Hypercall Interface: Allows XNU guest code to communicate with the host scheduler. +2. Thread Mapping: Maps XNU thread structures to host-managed ThreadHandles. +3. Cooperative Multitasking: XNU guest code yields control to the scheduler at appropriate points. + +## Key Features + +1. Deterministic Execution: Enables reproducible fuzzing sessions. +2. Fuzzer-Driven Scheduling: Allows exploration of different thread interleavings. +3. Full Threading Support: Simulates real kernel threading behavior. +4. Cooperative Multitasking: Ensures controlled execution of XNU guest code. + +## Usage in SockFuzzer + +The custom scheduler is used throughout the fuzzing process: + +1. Initialization: Set up initial XNU guest threads. +2. Fuzzing Loop: Manage thread execution during fuzz test runs. +3. Crash Analysis: Provide thread state information for crash reports. + +## Limitations and Future Work + +- Performance Optimization: Further improve scheduling efficiency. +- Advanced Scheduling Policies: Implement more sophisticated scheduling algorithms. +- Race Condition Detection: Enhance capabilities to detect and reproduce race conditions. \ No newline at end of file diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..7fdd09a --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,100 @@ +# SockFuzzer Unit Testing + +## Overview + +This project aims to test various components of the XNU kernel in a Linux user-mode environment. We use a combination of custom fake implementations, real XNU code, and a shared testing framework to achieve this goal. The structure accommodates the different subsystems of XNU (e.g., BSD, OSFMK) and their unique build requirements while maximizing code reuse where possible. + +## Key Components + +1. XNU Source Code: Located in `third_party/xnu/` +2. Fake Implementations: + - OSFMK: `fuzz/xnu/osfmk/fakes/` + - BSD: `fuzz/xnu/bsd/fakes/` + - (Other XNU subsystems follow a similar pattern) +3. Test Implementations: + - OSFMK: `fuzz/xnu/osfmk/test/` + - BSD: `fuzz/xnu/bsd/test/` + - (Other XNU subsystems follow a similar pattern) +4. Common Test Utilities: `fuzz/xnu/common/test_utils.h` and `fuzz/xnu/common/test_utils.c` +5. Shared Test Runner: `fuzz/host/test_runner.c` +6. Build System: Using Bazel + +## Testing Framework + +We use cmocka as our primary testing framework for its C compatibility, lightweight nature, and mocking capabilities. However, to handle the unique requirements of testing XNU components, we've implemented a custom assertion mechanism that works within the XNU environment and integrates with cmocka in the test runner. + +## Test Structure + +Our test structure consists of: + +1. Common Test Utilities (`fuzz/xnu/common/test_utils.h` and `test_utils.c`): Provides a `TestResult` structure and custom assertion macros that can be used within XNU subsystem tests. +2. Subsystem-specific Test Implementations (e.g., `fuzz/xnu/osfmk/test/osfmk_test_impl.c`): Contain the actual test logic that interacts with XNU code for each subsystem, using the common test utilities. +3. Subsystem-specific Test Headers (e.g., `fuzz/xnu/osfmk/test/osfmk_test_impl.h`): Declare the test functions implemented in the corresponding test implementation files. +4. Shared Test Runner (`fuzz/host/test_runner.c`): Contains the main cmocka test runner, calls the XNU subsystem tests, and integrates their results with cmocka's reporting mechanism. + +This approach allows us to: +- Isolate XNU-specific code and its dependencies in subsystem-specific implementations. +- Use a consistent assertion and result reporting mechanism across all XNU subsystem tests. +- Integrate XNU test results with cmocka's test runner and reporting features. +- Share common test setup, teardown, and utility functions across all subsystems. +- Simplify the addition of new tests for different subsystems. + +## Build Configuration + +We use the following Bazel targets: + +1. Common Test Utilities: A cc_library target that provides the TestResult structure and assertion utilities. +2. Subsystem-specific Test Implementation Libraries: cc_library targets that include XNU headers, fake implementations, and the common test utilities for each subsystem. +3. Shared Test Runner: A cc_library that includes cmocka and common test utilities. +4. Test Executable: A cc_test target that links the shared test runner with subsystem-specific test implementations. + +## Directory Structure + +``` +fuzz/ +├── host/ +│ ├── BUILD.bazel +│ └── test_runner.c +└── xnu/ + ├── common/ + │ ├── test_utils.h + │ └── test_utils.c + ├── bsd/ + │ └── fakes/ + │ └── (BSD-specific fake implementations) + └── osfmk/ + ├── BUILD.bazel + ├── fakes/ + │ └── (OSFMK-specific fake implementations) + └── test/ + ├── osfmk_test_impl.c + └── osfmk_test_impl.h +``` + +## Assumptions and Design Decisions + +1. XNU Code Isolation: XNU code can be compiled and run in a Linux user-mode environment with appropriate fake implementations. +2. Subsystem Separation: Each XNU subsystem has its own directory for fakes and tests due to different build requirements and header dependencies. +3. Common Test Utilities: Assertion and result reporting mechanisms are shared across all XNU subsystem tests. +4. Shared Test Runner: Common test functionality and integration with cmocka are shared across all subsystems. +5. Header Usage: We use headers from `third_party/xnu/` directly in test implementations. +6. Fake Implementations: Treated as hidden implementation details in the test code. +7. Compilation Flag Isolation: Test implementation headers are separated from their implementation to avoid propagating XNU-specific compilation flags to the test runner. + +## Guidelines for Contributors + +1. Familiarize yourself with the XNU subsystem you're working on and its API. +2. Understand the separation between the shared test runner, common test utilities, and subsystem-specific test implementations. +3. When adding new tests: + - Implement the test logic in the appropriate subsystem's `*_test_impl.c` file. + - Use the `XNU_ASSERT` macro from the common test utilities for assertions. + - Declare the test function in the corresponding `*_test_impl.h` file. + - Add the new test case to the `tests` array in the shared test runner (`fuzz/host/test_runner.c`). + - Ensure you're working in the correct subsystem directory (osfmk, bsd, etc.) for subsystem-specific code. +4. Ensure all XNU-specific code is isolated in the subsystem-specific test implementations. +5. Use fake implementations where necessary, but don't explicitly include them in test code. +6. When adding new fake implementations, place them in the appropriate subsystem's `fakes/` directory. +7. For common test utilities or setup/teardown functions, consider adding them to the common test utilities or the shared test runner as appropriate. +8. When adding new subsystem tests, update the `BUILD.bazel` files accordingly: + - Add new `cc_library` targets for test implementation and headers in the subsystem's `BUILD.bazel` file. + - Update the `deps` in the test executable target in `fuzz/host/BUILD.bazel` to include the new test implementation. \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..2684eab --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1721562059, + "narHash": "sha256-Tybxt65eyOARf285hMHIJ2uul8SULjFZbT9ZaEeUnP8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "68c9ed8bbed9dfce253cc91560bf9043297ef2fe", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4dd08b1 --- /dev/null +++ b/flake.nix @@ -0,0 +1,55 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: let + ccWrapper = (self: super: { + cc-wrapper = + import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/879d59508d54c7fde5c61ab41eefaf5f4b18472d.tar.gz"; + sha256 = "RTcsZoO5C7V42Rp2ZXNGQhjt8CS4OSB3JOlBfVb4PWE="; + }) { + inherit (super) stdenv gccUnwrapped clangUnwrapped; + lib = super.lib; + }; + }); + pkgs = import nixpkgs { + inherit system; + overlays = [ ccWrapper ]; + }; + in { + devShells.default = pkgs.mkShellNoCC { + name = "rules_nixpkgs_shell"; + packages = with pkgs; [ + bazel + cacert + nix + git + nodejs + kubectl + go + llvmPackages_18.libstdcxxClang + stdenv.cc.cc.lib + coreutils + gdb + rr + ]; + shellHook = '' + # Enable history substring search + bind '"\e[A": history-search-backward' + bind '"\e[B": history-search-forward' + bind '"\eOA": history-search-backward' + bind '"\eOB": history-search-forward' + + alias reset='tput reset' + # TODO(nedwill): is there a better way to do this? + export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath [ + pkgs.stdenv.cc.cc + ]} + export ASAN_SYMBOLIZER_PATH=${pkgs.llvmPackages_18.libllvm}/bin/llvm-symbolizer + ''; + }; + }); +} \ No newline at end of file diff --git a/fuzz/README b/fuzz/README new file mode 100644 index 0000000..86b46a4 --- /dev/null +++ b/fuzz/README @@ -0,0 +1,14 @@ +Directories + +* common + * Code shared between targets +* concurrence + * Adapter for Host +* host + * Headers for "hypercalls" into host environment from the fuzzer +* targets + * mach_port_fuzzer +* test + * Integration tests +* xnu + * SockFuzzer code that links into the XNU binary symbol space \ No newline at end of file diff --git a/fuzz/api/backend.c b/fuzz/api/backend.c deleted file mode 100644 index b7f3e31..0000000 --- a/fuzz/api/backend.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#include -#include - -#include "bsd/sys/_types/_size_t.h" -#include "bsd/sys/kpi_mbuf.h" -#include "bsd/sys/kpi_socket.h" -#include "bsd/sys/malloc.h" -#include "bsd/sys/protosw.h" -#include "bsd/sys/resourcevar.h" - -extern ifnet_t lo_ifp; - -void kernel_startup_bootstrap(); -void inpcb_timeout(void*, void*); -void key_timehandler(void); -void frag_timeout(void); -void nd6_slowtimo(void); -void nd6_timeout(void); -void in6_tmpaddrtimer(void); -void mp_timeout(void); -void igmp_timeout(void); -void tcp_fuzzer_reset(void); -void in6_rtqtimo(void*); -void frag6_timeout(); -void mld_timeout(); -bool ioctl_wrapper(unsigned long com); -void ip_input(mbuf_t m); -void ip6_input(mbuf_t m); -struct mbuf* mbuf_create(const uint8_t* data, size_t size, bool is_header, - bool force_ext, int m_type, int pktflags); -void mcache_init(void); -void mbinit(void); -void eventhandler_init(void); -void dlil_init(void); -void socketinit(void); -void domaininit(void); -void domain_timeout(void*); -void loopattach(void); -void ether_family_init(void); -void tcp_cc_init(void); -void net_init_run(void); -errno_t necp_init(void); -void in_rtqtimo(void* targ); -void* nstat_idle_check(void* p0, void* p1); - -extern int dlil_verbose; - -struct proc proc0; -struct filedesc filedesc0; -struct plimit plimit0; -proc_t kernproc; -int cmask = CMASK; - -__attribute__((visibility("default"))) bool init_proc(void) { - kernproc = &proc0; - kernproc->p_fd = &filedesc0; - // Permitting 10 open files should be more than enough - // without blowing up execution time. If you change this - // number you probably want to change the fd enum in the - // protobuf file. - plimit0.pl_rlimit[RLIMIT_NOFILE].rlim_cur = 10; - plimit0.pl_rlimit[RLIMIT_NOFILE].rlim_max = 10; - kernproc->p_limit = &plimit0; - filedesc0.fd_cmask = cmask; - filedesc0.fd_knlistsize = -1; - filedesc0.fd_knlist = NULL; - filedesc0.fd_knhash = NULL; - filedesc0.fd_knhashmask = 0; - // Increase sb_max - sb_max = 8192*1024 * 4; - dlil_verbose = 0; - return true; -} - -// TODO: expose these clears to the net protobuf -__attribute__((visibility("default"))) void clear_all() { - inpcb_timeout(NULL, NULL); - key_timehandler(); - frag_timeout(); - nd6_slowtimo(); - nd6_timeout(); - in6_tmpaddrtimer(); - mp_timeout(); - igmp_timeout(); - tcp_fuzzer_reset(); - frag6_timeout(); - mld_timeout(); - - // this adds work to the work queue - in6_rtqtimo(NULL); - in_rtqtimo(NULL); - - // TODO(nedwill): nd6_dad_timer - nstat_idle_check(NULL, NULL); - domain_timeout(NULL); -} - -#define MT_DATA 1 - -__attribute__((visibility("default"))) struct mbuf* get_mbuf_data( - const char* data, size_t size, int pktflags) { - struct mbuf* mbuf_data = - mbuf_create((const uint8_t*)data, size, true, false, MT_DATA, pktflags); - - // TODO(nedwill): consider using a non-loopback interface - // This indicates where the packet came from. - mbuf_pkthdr_setrcvif((mbuf_t)mbuf_data, lo_ifp); - return mbuf_data; -} - -extern unsigned long ioctls[]; -extern int num_ioctls; - -__attribute__((visibility("default"))) bool initialize_network() { - kernel_startup_bootstrap(); - kernel_startup_initialize_upto(STARTUP_SUB_EARLY_BOOT); - mcache_init(); - mbinit(); - eventhandler_init(); - dlil_init(); - socketinit(); - domaininit(); - loopattach(); - ether_family_init(); - tcp_cc_init(); - net_init_run(); - int res = necp_init(); - assert(!res); - return true; -} - -__attribute__((visibility("default"))) void ip_input_wrapper(void* m) { - ip_input((mbuf_t)m); -} - -__attribute__((visibility("default"))) void ip6_input_wrapper(void* m) { - ip6_input((mbuf_t)m); -} diff --git a/fuzz/api/syscall_wrappers.c b/fuzz/api/syscall_wrappers.c deleted file mode 100644 index 9b4e154..0000000 --- a/fuzz/api/syscall_wrappers.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#include "fuzz/api/syscall_wrappers.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wint-conversion" - -__attribute__((visibility("default"))) int accept_wrapper(int s, caddr_t name, - socklen_t* anamelen, - int* retval) { - struct accept_args uap = { - .s = s, - .name = name, - .anamelen = anamelen, - }; - return accept(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int accept_nocancel_wrapper( - int s, caddr_t name, socklen_t* anamelen, int* retval) { - struct accept_nocancel_args uap = { - .s = s, - .name = name, - .anamelen = anamelen, - }; - return accept_nocancel(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int bind_wrapper(int s, caddr_t name, - socklen_t namelen, - int* retval) { - struct bind_args uap = { - .s = s, - .name = name, - .namelen = namelen, - }; - return bind(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int close_wrapper(int fd, int* retval) { - struct close_args uap = { - .fd = fd, - }; - return sys_close(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int connect_wrapper(int s, caddr_t name, - socklen_t namelen, - int* retval) { - struct connect_args uap = { - .s = s, - .name = name, - .namelen = namelen, - }; - return connect(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int connect_nocancel_wrapper( - int s, caddr_t name, socklen_t namelen, int* retval) { - struct connect_nocancel_args uap = { - .s = s, - .name = name, - .namelen = namelen, - }; - return connect_nocancel(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int connectx_wrapper( - int socket, const sa_endpoints_t* endpoints, sae_associd_t associd, - unsigned int flags, const struct iovec* iov, unsigned int iovcnt, - size_t* len, sae_connid_t* connid, int* retval) { - struct connectx_args uap = { - .socket = socket, - .endpoints = endpoints, - .associd = associd, - .flags = flags, - .iov = iov, - .iovcnt = iovcnt, - .len = len, - .connid = connid, - }; - return connectx(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int disconnectx_wrapper( - int s, sae_associd_t aid, sae_connid_t cid, int* retval) { - struct disconnectx_args uap = { - .s = s, - .aid = aid, - .cid = cid, - }; - return disconnectx(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int getpeername_wrapper(int fdes, - caddr_t asa, - socklen_t* alen, - int* retval) { - struct getpeername_args uap = { - .fdes = fdes, - .asa = asa, - .alen = alen, - }; - return getpeername(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int getsockname_wrapper(int fdes, - caddr_t asa, - socklen_t* alen, - int* retval) { - struct getsockname_args uap = { - .fdes = fdes, - .asa = asa, - .alen = alen, - }; - return getsockname(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int getsockopt_wrapper( - int s, int level, int name, caddr_t val, socklen_t* avalsize, int* retval) { - struct getsockopt_args uap = { - .s = s, - .level = level, - .name = name, - .val = val, - .avalsize = avalsize, - }; - return getsockopt(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int ioctl_wrapper(int fd, u_long com, - caddr_t data, - int* retval) { - struct ioctl_args uap = { - .fd = fd, - .com = com, - .data = data, - }; - return ioctl(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int listen_wrapper(int s, int backlog, - int* retval) { - struct listen_args uap = { - .s = s, - .backlog = backlog, - }; - return listen(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int necp_client_action_wrapper( - int necp_fd, uint32_t action, uuid_t client_id, size_t client_id_len, - uint8_t* buffer, size_t buffer_size, int* retval) { - struct necp_client_action_args uap = { - .necp_fd = necp_fd, - .action = action, - .client_id = client_id, - .client_id_len = client_id_len, - .buffer = buffer, - .buffer_size = buffer_size, - }; - return necp_client_action(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int necp_match_policy_wrapper( - uint8_t* parameters, size_t parameters_size, - struct necp_aggregate_result* returned_result, int* retval) { - struct necp_match_policy_args uap = { - .parameters = parameters, - .parameters_size = parameters_size, - .returned_result = returned_result, - }; - return necp_match_policy(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int necp_open_wrapper(int flags, - int* retval) { - struct necp_open_args uap = { - .flags = flags, - }; - return necp_open(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int necp_session_action_wrapper( - int necp_fd, uint32_t action, uint8_t* in_buffer, size_t in_buffer_length, - uint8_t* out_buffer, size_t out_buffer_length, int* retval) { - struct necp_session_action_args uap = { - .necp_fd = necp_fd, - .action = action, - .in_buffer = in_buffer, - .in_buffer_length = in_buffer_length, - .out_buffer = out_buffer, - .out_buffer_length = out_buffer_length, - }; - return necp_session_action(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int necp_session_open_wrapper( - int flags, int* retval) { - struct necp_session_open_args uap = { - .flags = flags, - }; - return necp_session_open(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int peeloff_wrapper(int s, - sae_associd_t aid, - int* retval) { - struct peeloff_args uap = { - .s = s, - .aid = aid, - }; - return peeloff(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int pipe_wrapper(int* retval) { - struct pipe_args uap = {}; - return pipe(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int recvfrom_wrapper( - int s, void* buf, size_t len, int flags, struct sockaddr* from, - int* fromlenaddr, int* retval) { - struct recvfrom_args uap = { - .s = s, - .buf = buf, - .len = len, - .flags = flags, - .from = from, - .fromlenaddr = fromlenaddr, - }; - return recvfrom(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int recvfrom_nocancel_wrapper( - int s, void* buf, size_t len, int flags, struct sockaddr* from, - int* fromlenaddr, int* retval) { - struct recvfrom_nocancel_args uap = { - .s = s, - .buf = buf, - .len = len, - .flags = flags, - .from = from, - .fromlenaddr = fromlenaddr, - }; - return recvfrom_nocancel(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int recvmsg_wrapper(int s, - struct msghdr* msg, - int flags, - int* retval) { - struct recvmsg_args uap = { - .s = s, - .msg = msg, - .flags = flags, - }; - return recvmsg(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int recvmsg_nocancel_wrapper( - int s, struct msghdr* msg, int flags, int* retval) { - struct recvmsg_nocancel_args uap = { - .s = s, - .msg = msg, - .flags = flags, - }; - return recvmsg_nocancel(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int recvmsg_x_wrapper( - int s, struct msghdr_x* msgp, u_int cnt, int flags, user_ssize_t* retval) { - struct recvmsg_x_args uap = { - .s = s, - .msgp = msgp, - .cnt = cnt, - .flags = flags, - }; - return recvmsg_x(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int sendmsg_wrapper(int s, caddr_t msg, - int flags, - int* retval) { - struct sendmsg_args uap = { - .s = s, - .msg = msg, - .flags = flags, - }; - return sendmsg(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int sendmsg_nocancel_wrapper( - int s, caddr_t msg, int flags, int* retval) { - struct sendmsg_nocancel_args uap = { - .s = s, - .msg = msg, - .flags = flags, - }; - return sendmsg_nocancel(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int sendmsg_x_wrapper( - int s, struct msghdr_x* msgp, u_int cnt, int flags, user_ssize_t* retval) { - struct sendmsg_x_args uap = { - .s = s, - .msgp = msgp, - .cnt = cnt, - .flags = flags, - }; - return sendmsg_x(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int sendto_wrapper(int s, caddr_t buf, - size_t len, int flags, - caddr_t to, - socklen_t tolen, - int* retval) { - struct sendto_args uap = { - .s = s, - .buf = buf, - .len = len, - .flags = flags, - .to = to, - .tolen = tolen, - }; - return sendto(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int sendto_nocancel_wrapper( - int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen, - int* retval) { - struct sendto_nocancel_args uap = { - .s = s, - .buf = buf, - .len = len, - .flags = flags, - .to = to, - .tolen = tolen, - }; - return sendto_nocancel(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int setsockopt_wrapper( - int s, int level, int name, caddr_t val, socklen_t valsize, int* retval) { - struct setsockopt_args uap = { - .s = s, - .level = level, - .name = name, - .val = val, - .valsize = valsize, - }; - return setsockopt(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int shutdown_wrapper(int s, int how, - int* retval) { - struct shutdown_args uap = { - .s = s, - .how = how, - }; - return shutdown(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int socket_wrapper(int domain, int type, - int protocol, - int* retval) { - struct socket_args uap = { - .domain = domain, - .type = type, - .protocol = protocol, - }; - return socket(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int socket_delegate_wrapper( - int domain, int type, int protocol, pid_t epid, int* retval) { - struct socket_delegate_args uap = { - .domain = domain, - .type = type, - .protocol = protocol, - .epid = epid, - }; - return socket_delegate(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int socketpair_wrapper( - int domain, int type, int protocol, int* rsv, int* retval) { - struct socketpair_args uap = { - .domain = domain, - .type = type, - .protocol = protocol, - .rsv = rsv, - }; - return socketpair(kernproc, &uap, retval); -} - -#pragma clang diagnostic pop diff --git a/fuzz/api/syscall_wrappers.h b/fuzz/api/syscall_wrappers.h deleted file mode 100644 index 4fd5ade..0000000 --- a/fuzz/api/syscall_wrappers.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#ifndef SYSCALL_WRAPPERS_H_ -#define SYSCALL_WRAPPERS_H_ - -#ifdef LIBXNU_BUILD -#include "BUILD/obj/DEBUG_X86_64/bsd/sys/sysproto.h" -#include "bsd/net/necp.h" -#include "bsd/netinet/in.h" -#include "bsd/sys/socket.h" -#else -typedef __uint64_t user64_addr_t; -typedef struct user64_sa_endpoints sa_endpoints_t; -typedef unsigned char uuid_t[16]; -typedef u_int64_t user_addr_t; -typedef u_int32_t socklen_t; -typedef __uint32_t sae_associd_t; -typedef __uint32_t sae_connid_t; -typedef int64_t user_ssize_t; -typedef char* caddr_t; -struct user64_sa_endpoints { - unsigned int sae_srcif; /* optional source interface */ - user64_addr_t sae_srcaddr; /* optional source address */ - socklen_t sae_srcaddrlen; /* size of source address */ - user64_addr_t sae_dstaddr; /* destination address */ - socklen_t sae_dstaddrlen; /* size of destination address */ -}; -struct user64_msghdr { - user64_addr_t msg_name; /* optional address */ - socklen_t msg_namelen; /* size of address */ - user64_addr_t msg_iov; /* scatter/gather array */ - int msg_iovlen; /* # elements in msg_iov */ - user64_addr_t msg_control; /* ancillary data, see below */ - socklen_t msg_controllen; /* ancillary data buffer len */ - int msg_flags; /* flags on received message */ -}; -#endif -#include -#include - -int accept_wrapper(int s, caddr_t name, socklen_t* anamelen, int* retval); -int accept_nocancel_wrapper(int s, caddr_t name, socklen_t* anamelen, - int* retval); -int bind_wrapper(int s, caddr_t name, socklen_t namelen, int* retval); -int close_wrapper(int fd, int* retval); -int connect_wrapper(int s, caddr_t name, socklen_t namelen, int* retval); -int connect_nocancel_wrapper(int s, caddr_t name, socklen_t namelen, - int* retval); -int connectx_wrapper(int socket, const sa_endpoints_t* endpoints, - sae_associd_t associd, unsigned int flags, - const struct iovec* iov, unsigned int iovcnt, size_t* len, - sae_connid_t* connid, int* retval); -int disconnectx_wrapper(int s, sae_associd_t aid, sae_connid_t cid, - int* retval); -int getpeername_wrapper(int fdes, caddr_t asa, socklen_t* alen, int* retval); -int getsockname_wrapper(int fdes, caddr_t asa, socklen_t* alen, int* retval); -int getsockopt_wrapper(int s, int level, int name, caddr_t val, - socklen_t* avalsize, int* retval); -int ioctl_wrapper(int fd, u_long com, caddr_t data, int* retval); -int listen_wrapper(int s, int backlog, int* retval); -int necp_client_action_wrapper(int necp_fd, uint32_t action, uuid_t client_id, - size_t client_id_len, uint8_t* buffer, - size_t buffer_size, int* retval); -int necp_match_policy_wrapper(uint8_t* parameters, size_t parameters_size, - struct necp_aggregate_result* returned_result, - int* retval); -int necp_open_wrapper(int flags, int* retval); -int necp_session_action_wrapper(int necp_fd, uint32_t action, - uint8_t* in_buffer, size_t in_buffer_length, - uint8_t* out_buffer, size_t out_buffer_length, - int* retval); -int necp_session_open_wrapper(int flags, int* retval); -int peeloff_wrapper(int s, sae_associd_t aid, int* retval); -int pipe_wrapper(int* retval); -int recvfrom_wrapper(int s, void* buf, size_t len, int flags, - struct sockaddr* from, int* fromlenaddr, int* retval); -int recvfrom_nocancel_wrapper(int s, void* buf, size_t len, int flags, - struct sockaddr* from, int* fromlenaddr, - int* retval); -int recvmsg_wrapper(int s, struct msghdr* msg, int flags, int* retval); -int recvmsg_nocancel_wrapper(int s, struct msghdr* msg, int flags, int* retval); -int recvmsg_x_wrapper(int s, struct msghdr_x* msgp, u_int cnt, int flags, - user_ssize_t* retval); -int sendmsg_wrapper(int s, caddr_t msg, int flags, int* retval); -int sendmsg_nocancel_wrapper(int s, caddr_t msg, int flags, int* retval); -int sendmsg_x_wrapper(int s, struct msghdr_x* msgp, u_int cnt, int flags, - user_ssize_t* retval); -int sendto_wrapper(int s, caddr_t buf, size_t len, int flags, caddr_t to, - socklen_t tolen, int* retval); -int sendto_nocancel_wrapper(int s, caddr_t buf, size_t len, int flags, - caddr_t to, socklen_t tolen, int* retval); -int setsockopt_wrapper(int s, int level, int name, caddr_t val, - socklen_t valsize, int* retval); -int shutdown_wrapper(int s, int how, int* retval); -int socket_wrapper(int domain, int type, int protocol, int* retval); -int socket_delegate_wrapper(int domain, int type, int protocol, pid_t epid, - int* retval); -int socketpair_wrapper(int domain, int type, int protocol, int* rsv, - int* retval); - -#endif // SYSCALL_WRAPPERS_H_ diff --git a/fuzz/common/BUILD.bazel b/fuzz/common/BUILD.bazel new file mode 100644 index 0000000..954449e --- /dev/null +++ b/fuzz/common/BUILD.bazel @@ -0,0 +1,21 @@ +cc_library( + name = "utility", + srcs = ["utility.cc"], + hdrs = ["utility.h"], + visibility = [ + "//fuzz:__subpackages__", + ], + deps = [ + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:str_format", + ], +) + +cc_test( + name = "utility_test", + srcs = ["utility_test.cc"], + deps = [ + ":utility", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fuzz/common/utility.cc b/fuzz/common/utility.cc new file mode 100644 index 0000000..860b4ba --- /dev/null +++ b/fuzz/common/utility.cc @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC +// +// 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 +#include +#include +#include +#include + +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" + +absl::StatusOr> ReadFile(const std::string& filename) { + if (!std::filesystem::exists(filename)) { + return absl::NotFoundError("File does not exist: " + filename); + } + std::ifstream input_fstream(filename, std::ios::binary); + return std::vector(std::istreambuf_iterator(input_fstream), + std::istreambuf_iterator()); +} + +std::string DumpVector(const std::vector& data) { + std::stringstream out; + out << "{"; + for (auto it = data.begin(); it != data.end(); ++it) { + if (it != data.begin()) { + out << ", "; + } + out << std::to_string(*it); + } + out << "}"; + return out.str(); +} diff --git a/fuzz/common/utility.h b/fuzz/common/utility.h new file mode 100644 index 0000000..a530647 --- /dev/null +++ b/fuzz/common/utility.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_COMMON_UTILITY_H_ +#define FUZZ_COMMON_UTILITY_H_ + +#include + +#include "absl/status/statusor.h" + +absl::StatusOr> ReadFile(const std::string& filename); + +std::string DumpVector(const std::vector& v); + +#endif /* FUZZ_COMMON_UTILITY_H_ */ diff --git a/fuzz/common/utility_test.cc b/fuzz/common/utility_test.cc new file mode 100644 index 0000000..3c2884a --- /dev/null +++ b/fuzz/common/utility_test.cc @@ -0,0 +1,32 @@ +// Copyright 2024 Google LLC +// +// 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 "fuzz/common/utility.h" + +#include + +TEST(VectorDumpTest, BasicTest) { + std::vector v = {1, 2, 3, 4, 5}; + EXPECT_EQ("{1, 2, 3, 4, 5}", DumpVector(v)); +} + +TEST(VectorDumpTest, EmptyTest) { + std::vector v; + EXPECT_EQ("{}", DumpVector(v)); +} + +TEST(VectorDumpTest, LargeTest) { + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + EXPECT_EQ("{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}", DumpVector(v)); +} diff --git a/fuzz/executor/BUILD.bazel b/fuzz/executor/BUILD.bazel index 7213f3d..4cf4c2e 100644 --- a/fuzz/executor/BUILD.bazel +++ b/fuzz/executor/BUILD.bazel @@ -1,17 +1,3 @@ -# Copyright 2022 Google LLC -# -# 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. - cc_library( name = "headers", srcs = ["coroutine_executor.h"], @@ -32,8 +18,8 @@ cc_library( ], deps = [ ":headers", - "//executor", - "//scheduler:fuzzed_scheduler", + "//third_party/concurrence/executor", + "//third_party/concurrence/scheduler:fuzzed_scheduler", "@com_google_absl//absl/container:flat_hash_map", ], ) @@ -44,8 +30,8 @@ cc_test( srcs = ["coroutine_executor_test.cc"], deps = [ ":coroutine_executor", - "//executor:executor_test_template", - "//scheduler:fuzzed_scheduler_test_template", + "//third_party/concurrence/executor:executor_test_template", + "//third_party/concurrence/scheduler:fuzzed_scheduler_test_template", "@com_google_googletest//:gtest_main", ], ) diff --git a/fuzz/executor/coroutine_executor.cc b/fuzz/executor/coroutine_executor.cc index 0a4fbf7..c7ce4c3 100644 --- a/fuzz/executor/coroutine_executor.cc +++ b/fuzz/executor/coroutine_executor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,25 +25,34 @@ #include #endif -const int KERNEL_STACK_SIZE = 4096 * 16; +const int KERNEL_STACK_SIZE = 4096 * 32; -#include "third_party/libco/libco.h" +extern bool is_verbose; + +#include #include #include #include -static CoroutineExecutor *g_coroutine_executor; +namespace { + +// Used to access CoroutineExecutor from cothreads +CoroutineExecutor *g_coroutine_executor; -static void ThreadStart() { +void ThreadStart() { g_coroutine_executor->CallPendingFunctionThenSwap(); } +} + CoroutineExecutor::CoroutineExecutor() : main_thread_(co_active()) { g_current_thread = reinterpret_cast(co_active()); g_coroutine_executor = this; } -CoroutineExecutor::~CoroutineExecutor() { g_coroutine_executor = nullptr; } +CoroutineExecutor::~CoroutineExecutor() { + g_coroutine_executor = nullptr; +} ThreadHandle CoroutineExecutor::CreateThread(std::function target) { void *mapping = @@ -106,6 +115,10 @@ void CoroutineExecutor::SwitchTo(ThreadHandle handle) { return; } + if (is_verbose) { + SetBacktrace(GetCurrentThreadHandle()); + } + #if __has_feature(address_sanitizer) // TODO(nedwill): track first argument to support stack use after return // detection diff --git a/fuzz/executor/coroutine_executor.h b/fuzz/executor/coroutine_executor.h index dca61f4..c965a9c 100644 --- a/fuzz/executor/coroutine_executor.h +++ b/fuzz/executor/coroutine_executor.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,26 @@ #ifndef COROUTINE_EXECUTOR_H_ #define COROUTINE_EXECUTOR_H_ +#include + #include +#include +#include #include +#include #include "absl/container/flat_hash_map.h" -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" #include "third_party/libco/libco.h" class CoroutineExecutor : public Executor { public: explicit CoroutineExecutor(); ~CoroutineExecutor() override; + CoroutineExecutor(const CoroutineExecutor &) = delete; + CoroutineExecutor(CoroutineExecutor &&) = delete; + CoroutineExecutor &operator=(const CoroutineExecutor &) = delete; + CoroutineExecutor &operator=(CoroutineExecutor &&) = delete; ThreadHandle CreateThread(std::function target) override; void DeleteThread(ThreadHandle handle) override; diff --git a/fuzz/executor/coroutine_executor_test.cc b/fuzz/executor/coroutine_executor_test.cc index 18f9fc0..fa2dff7 100644 --- a/fuzz/executor/coroutine_executor_test.cc +++ b/fuzz/executor/coroutine_executor_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,19 +14,23 @@ #include "fuzz/executor/coroutine_executor.h" -#include "executor/executor_test_template.h" +#include + #include "gtest/gtest.h" -#include "scheduler/fuzzed_scheduler.h" -#include "scheduler/fuzzed_scheduler_test_template.h" +#include "third_party/concurrence/executor/executor_test_template.h" +#include "third_party/concurrence/scheduler/fuzzed_scheduler.h" +#include "third_party/concurrence/scheduler/fuzzed_scheduler_test_template.h" class Scheduler; -Executor *g_executor; +bool is_verbose = false; + +Executor* g_executor; -Scheduler *g_scheduler; +Scheduler* g_scheduler; template <> -Executor *CreateExecutor() { +Executor* CreateExecutor() { return new CoroutineExecutor; } @@ -37,7 +41,7 @@ INSTANTIATE_TYPED_TEST_SUITE_P(CoroutineExecutorTests, ExecutorTest, ExecutorImplementation); template <> -Scheduler *CreateScheduler() { +Scheduler* CreateScheduler() { return new FuzzedScheduler(new CoroutineExecutor, new EmptySchedulerCallbacks); } diff --git a/fuzz/fakes/fake_impls.c b/fuzz/fakes/fake_impls.c deleted file mode 100644 index b8b5358..0000000 --- a/fuzz/fakes/fake_impls.c +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -// Trivial implementations belong here. More substantial faked -// subsystems should live in their own file. - -#include -#include -#include - -#include -#include - -#include "bsd/net/nwk_wq.h" -#include "bsd/sys/_types/_timeval.h" -#include "bsd/sys/conf.h" -#include "bsd/sys/kdebug_kernel.h" -#include "bsd/sys/kernel_types.h" -#include "bsd/sys/malloc.h" -#include "bsd/sys/resource.h" -#include "bsd/uuid/uuid.h" - -extern void get_fuzzed_bytes(void* addr, size_t bytes); -extern bool get_fuzzed_bool(void); - -int snprintf(char*, size_t, const char*, ...) __printflike(3, 4); - -int maxfilesperproc = 10; - -bool PE_parse_boot_argn(const char* arg_string, void* arg_ptr, int max_arg) { - if (!strcmp(arg_string, "ifa_debug")) { - *(int*)arg_ptr = 0; - return true; - } - - if (!strcmp(arg_string, "inaddr_nhash")) { - *(uint32_t*)arg_ptr = 0; - return true; - } - - if (!strcmp(arg_string, "mcache_flags")) { - *(uint32_t*)arg_ptr = 0; - return true; - } - - if (!strcmp(arg_string, "mbuf_debug")) { - *(uint32_t*)arg_ptr = 0; - return true; - } - - if (!strcmp(arg_string, "mleak_sample_factor")) { - *(uint32_t*)arg_ptr = 0; - return true; - } - - // Just return 0 by default. - memset(arg_ptr, 0, max_arg); - - // assert(false); - return false; -} - -void* os_log_create() { return (void*)1; } - -void pflog_packet() {} - -// TODO(nedwill): return a real vfs context -void* vfs_context_current() { return NULL; } - -int csproc_get_platform_binary(void* p) { return 0; } - -void uuid_clear(uuid_t uu) { memset(uu, 0, sizeof(uuid_t)); } - -int uuid_is_null(const uuid_t uu) { - return !memcmp(uu, UUID_NULL, sizeof(uuid_t)); -} - -int uuid_compare(const uuid_t uu1, const uuid_t uu2) { - return memcmp(uu1, uu2, sizeof(uuid_t)); -} - -// TODO(nedwill): this shouldn't return the same value -// within the same fuzz session (use a counter and reset) -void uuid_generate_random(uuid_t out) { - if (get_fuzzed_bool()) { - memcpy(out, "0000000000000000", 16); - return; - } - if (get_fuzzed_bool()) { - memcpy(out, "1111111111111111", 16); - return; - } - memcpy(out, "2222222222222222", 16); -} - -void uuid_copy(uuid_t dst, const uuid_t src) { - memcpy(dst, src, sizeof(uuid_t)); -} - -void uuid_unparse_upper(const uuid_t uu, uuid_string_t out) { - snprintf(out, sizeof(uuid_string_t), - "%c%c%c%c-" - "%c%c-" - "%c%c-" - "%c%c-" - "%c%c%c%c%c%c", - uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9], - uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); -} - -void uuid_unparse(const uuid_t uu, uuid_string_t out) { - uuid_unparse_upper(uu, out); -} - -extern void* kernproc; - -void* vfs_context_proc() { return kernproc; } - -// TODO(Ned): better timekeeping here -uint64_t mach_continuous_time(void) { return 0; } - -// TODO: handle timer scheduling -void timeout() { assert(false); } - -void microtime(struct timeval* tvp) { memset(&tvp, 0, sizeof(tvp)); } - -void microuptime(struct timeval* tvp) { memset(&tvp, 0, sizeof(tvp)); } - -int mac_socket_check_accepted() { return 0; } - -int mac_socket_check_setsockopt() { return 0; } - -int mac_socket_check_bind() { return 0; } - -int mac_file_check_ioctl() { return 0; } - -int deflateInit2_() { return 1; } -int inflateInit2_() { return 1; } - -bool kauth_cred_issuser() { return true; } - -unsigned long RandomULong() { - // returning 0 here would be a failure - return 1; -} - -// TODO: threading -int kernel_thread_start() { return 0; } - -int cdevsw_add(int major, const struct cdevsw *cdevsw) { - return 0; -} - -void devfs_make_node() {} - -bool lck_mtx_try_lock() { return true; } - -void kprintf() { return; } - -void thread_deallocate() {} - -// we are root -int proc_suser() { return 0; } - -void _os_log_internal() {} - -void hw_atomic_add() {} - -void hw_atomic_sub() {} - -void lck_mtx_destroy() {} - -int mac_socket_check_ioctl() { return 0; } - -bool proc_is64bit() { return true; } - -int priv_check_cred() { return 0; } - -bool lck_rw_try_lock_exclusive() { return true; } - -void* malloc(size_t size); -void free(void* ptr); - -// TODO(nedwill): fix this hack -__attribute__((visibility("default"))) bool real_copyout = true; - -int copyout(const void* kaddr, user_addr_t udaddr, size_t len) { - // randomly fail - if (get_fuzzed_bool()) { - return 1; - } - - if (!udaddr || udaddr == 1 || !real_copyout) { - void* buf = malloc(len); - memcpy(buf, kaddr, len); - free(buf); - return 0; - } - - memcpy((void*)udaddr, kaddr, len); - return 0; -} - -void* __MALLOC(size_t size, int type, int flags, vm_allocation_site_t* site) { - void* addr = NULL; - assert(type < M_LAST); - - if (size == 0) { - return NULL; - } - - addr = malloc(size); - if (!addr) { - return NULL; - } - - if (flags & M_ZERO) { - bzero(addr, size); - } - - return (addr); -} - -void read_frandom(void* buffer, unsigned int numBytes) { - get_fuzzed_bytes(buffer, numBytes); -} - -void read_random(void* buffer, unsigned int numBytes) { - get_fuzzed_bytes(buffer, numBytes); -} - -int ml_get_max_cpus(void) { return 1; } - -void clock_interval_to_deadline(uint32_t interval, uint32_t scale_factor, - uint64_t* result) { - *result = 0; -} - -void clock_interval_to_absolutetime_interval(uint32_t interval, - uint32_t scale_factor, - uint64_t* result) { - *result = 0; -} - -void* thread_call_allocate_with_options() { return (void*)1; } - -bool thread_call_enter_delayed_with_leeway() { return true; } - -void lck_rw_assert() {} - -uint32_t IOMapperIOVMAlloc() { return 0; } - -int proc_uniqueid() { return 0; } - -uint64_t mach_absolute_time() { return 0; } - -int proc_pid() { return 0; } - -void proc_getexecutableuuid(void* p, unsigned char* uuidbuf, - unsigned long size) { - memset(uuidbuf, 0, size); -} - -void proc_pidoriginatoruuid(void* buffer, size_t size) { - memset(buffer, 0, size); -} - -void* kauth_cred_proc_ref() { return (void*)1; } - -void* kauth_cred_get() { return (void*)1; } - -void* proc_ucred() { return (void*)1; } - -int suser(void* arg1, void* arg2) { - (void)arg1; - (void)arg2; - return 0; -} - -void lck_rw_lock_shared() {} - -void lck_rw_done() {} - -bool proc_get_effective_thread_policy() { - // TODO: more options - return false; -} - -void* current_proc() { return kernproc; } - -int proc_selfpid() { return 1; } - -void tvtohz() {} - -int kauth_cred_getuid() { - // UUID: root - return get_fuzzed_bool() ? 1 : 0; -} - -char* proc_best_name() { return "kernproc"; } - -void* proc_find() { return kernproc; } - -int mac_socket_check_create() { return 0; } - -int mac_socket_check_accept() { return 0; } - -void ovbcopy(const char* from, char* to, size_t nbytes) { - memmove(to, from, nbytes); -} - -int __attribute__((warn_unused_result)) -copyin(const user_addr_t uaddr, void *kaddr, size_t len) { - // Address 1 means use fuzzed bytes, otherwise use real bytes. - // NOTE: this does not support nested useraddr. - if (uaddr != 1) { - memcpy(kaddr, (void*)uaddr, len); - return 0; - } - - if (get_fuzzed_bool()) { - return -1; - } - - get_fuzzed_bytes(kaddr, len); - return 0; -} - -void SHA1Final() {} - -void SHA1Init() {} - -void SHA1Update() {} - -void* thread_call_allocate_with_priority() { return (void*)1; } - -void lck_grp_attr_free() {} - -void lck_grp_free() {} - -void lck_rw_lock_exclusive() {} - -void timevaladd() {} -void timevalsub() {} - -void thread_call_enter_delayed() {} - -void MD5Init() {} -void MD5Update() {} -void MD5Final(unsigned char* digest, void* ctx) { - memset(digest, 0, 4); -} - -void proc_rele() {} -void wakeup(void* chan) {} - -void lck_spin_lock() {} -void lck_spin_unlock() {} -void kauth_cred_unref(void* cred) {} -void lck_rw_unlock_exclusive() {} - -bool IS_64BIT_PROCESS() { return true; } - -int mac_socket_check_listen() { return 0; } - -void kauth_cred_ref() {} - -void in_stat_set_activity_bitmap() {} - -int mac_socket_check_getsockopt() { return 0; } - -int mac_pipe_check_ioctl() { return 0; } - -int mac_pipe_check_write() { return 0; } - -int mac_pipe_check_kqfilter() { return 0; } - -int mac_pipe_label_init() { return 0; } - -int mac_pipe_label_destroy() { return 0; } - -int mac_pipe_check_read() { return 0; } - -int mac_pipe_check_stat() { return 0; } - -int mac_pipe_label_associate() { return 0; } - -int kauth_getuid() { return 0; } - -int kauth_getgid() { return 0; } - -int mac_pipe_check_select() { return 0; } - -void _aio_close() {} -void unlink1() {} - -int mac_socket_check_connect() { return 0; } - -void ml_thread_policy() {} - -void aes_encrypt_key128() {} - -void OSBacktrace() {} - -void lck_grp_attr_setdefault() {} - -void nanouptime() {} - -void wakeup_one() {} - -int lck_mtx_try_lock_spin() { return 1; } - -void absolutetime_to_nanoseconds(uint64_t in, uint64_t* out) { *out = 0; } - -void free(void* ptr); - -void nwk_wq_enqueue(struct nwk_wq_entry* nwk_item) { - nwk_item->func(nwk_item->arg); - free(nwk_item); -} - -int ppsratecheck() { return 1; } - -bool ratecheck() { return true; } - -void fulong() {} -void ubc_cs_blob_deallocate() {} -void proc_thread() {} -void munge_user32_stat64() {} -int mac_file_check_lock() { return 0; } -void vnode_setsize() {} -void vnode_setnocache() {} -void kauth_authorize_fileop() {} -void VNOP_FSYNC() {} -void tablefull() {} -void vnode_recycle() {} -void ipc_object_copyin() {} -int mac_file_check_inherit() { return 0; } -void vnode_vid() {} -void munge_user32_stat() {} -void VNOP_OFFTOBLK() {} -int mac_file_check_create() { return 0; } -void fileport_port_to_fileglob() {} -void VNOP_SETATTR() {} -void vfs_devblocksize() {} -int mac_file_check_library_validation() { return 0; } -void ubc_cs_blob_add() {} -void vn_getpath() {} -void ipc_port_release_send() {} -void proc_kqhashlock_grp() {} -void vn_path_package_check() {} -void VNOP_GETATTR() {} -void ubc_cs_blob_allocate() {} -void audit_sysclose() {} -void vnode_is_openevt() {} -void audit_arg_vnpath_withref() {} -int mac_file_check_fcntl() { return 0; } -void VNOP_ALLOCATE() {} -void fg_vn_data_free() {} -void VNOP_BLKTOOFF() {} -void vnode_islnk() {} -void VNOP_IOCTL() {} -int mac_vnode_check_truncate() { return 0; } -int mac_file_check_dup() { return 0; } -void ubc_cs_blob_get() {} -void audit_arg_vnpath() {} -void get_task_ipcspace() {} -void vn_rdwr() {} -int mac_file_label_destroy() { return 0; } -void fileport_alloc() {} -void vnode_getwithref() {} -int mac_file_label_associate() { return 0; } -void sulong() {} -void proc_lck_attr() {} -int mac_vnode_check_write() { return 0; } -void ipc_port_copyout_send() {} -void kauth_filesec_free() {} -void munge_user64_stat64() {} -void munge_user64_stat() {} -void VNOP_EXCHANGE() {} -void vnode_set_openevt() {} -void vn_stat_noauth() {} -void vnode_mount() {} -void open1() {} -void kauth_authorize_fileop_has_listeners() {} -void fp_isguarded() {} -void audit_arg_fflags() {} -int mac_vnode_notify_truncate() { return 0; } -void fp_guard_exception() {} -void vnode_clear_openevt() {} -void pshm_stat() {} -void proc_knhashlock_grp() {} -void VNOP_BLOCKMAP() {} -void vnode_clearnocache() {} -void VNOP_ADVLOCK() {} -void ubc_cs_blob_revalidate() {} -void guarded_fileproc_free() {} -void audit_arg_text() {} -void vnode_isnocache() {} -void mach_port_deallocate() {} -int mac_file_label_init() { return 0; } -void vn_pathconf() {} -void audit_arg_mode() {} -long boottime_sec() { return 0; } -void mac_socket_check_receive() {} -void mac_socket_check_send() {} - -void kernel_debug() {} - -void lck_rw_unlock_shared() {} -kern_return_t kmem_alloc_contig() { assert(false); } -uint32_t ipc_control_port_options; - -bool current_task_can_use_restricted_in_port() { return true; } - -unsigned int -ml_wait_max_cpus(void) -{ - return 1; -} - -int -fls(unsigned int mask) -{ - if (mask == 0) { - return 0; - } - - return (sizeof(mask) << 3) - __builtin_clz(mask); -} - -int scnprintf(char *buf, size_t size, const char *fmt, ...) { - return 0; -} - -rlim_t -proc_limitgetcur(proc_t p, int which, boolean_t to_lock_proc) { - if (which == RLIMIT_NOFILE) { - return 10; - } - assert(false); -} - -task_t proc_task() { return TASK_NULL; } - -vm_offset_t current_percpu_base(void) { - return 0; -} - -int proc_pidversion(proc_t p) { - assert(false); - return 0; -} - -unsigned int kdebug_enable = 0; -void kernel_debug_string_early(const char *message) { - printf("kernel_debug_string_early: %s\n", message); -} diff --git a/fuzz/fakes/mbuf.c b/fuzz/fakes/mbuf.c deleted file mode 100644 index 55199c4..0000000 --- a/fuzz/fakes/mbuf.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#include "bsd/machine/param.h" -#include "bsd/sys/mbuf.h" - -// We link these in from libc/asan -void* malloc(size_t size); -void* calloc(size_t nmemb, size_t size); -void free(void* ptr); -int posix_memalign(void** memptr, size_t alignment, size_t size); - -typedef struct { - size_t bufsize; - // Must we always return clusters? - bool cluster_cache; -} mcache_t; - -mcache_t* mcache_create(const char* name, size_t bufsize, size_t align, - u_int32_t flags, int wait) { - mcache_t* cache = (mcache_t*)malloc(sizeof(mcache_t)); - cache->bufsize = bufsize; - cache->cluster_cache = false; - return cache; -} - -// type doesn't match but we just need the address -void mbuf_cslab_alloc(void); - -mcache_t* mcache_create_ext(const char* name, size_t bufsize, void* allocfn, - void* freefn, void* auditfn, void* logfn, - void* notifyfn, void* arg, u_int32_t flags, - int wait) { - mcache_t* cache = (mcache_t*)malloc(sizeof(mcache_t)); - cache->bufsize = bufsize; - cache->cluster_cache = allocfn == mbuf_cslab_alloc; - return cache; -} - -void assfail() { assert(false); } - -struct mbuf* mbuf_create(const uint8_t* data, size_t size, bool is_header, - bool force_ext, int mtype, int pktflags) { - struct mbuf* m = NULL; - assert(sizeof(struct mbuf) == MSIZE); - if (posix_memalign((void**)&m, MSIZE, sizeof(struct mbuf))) { - return NULL; - } - memset((void*)m, 0, sizeof(struct mbuf)); - m->m_type = mtype; - // TODO: use fuzzed data to create all kinds of mbuf chains - size_t max_size = MBIGCLBYTES; - if (njcl > 0) { - max_size = njclbytes; - } - if (size >= max_size) { - size = max_size - 1; - } - - if (force_ext || size > sizeof(m->M_dat.MH.MH_dat.MH_databuf)) { - m->m_flags = M_EXT; - m->m_data = m->m_ext.ext_buf = (caddr_t)calloc(1, size); - m->m_ext.ext_size = size; - - struct ext_ref* rfa = (struct ext_ref*)calloc(1, sizeof(struct ext_ref)); - rfa->refcnt = 1; - rfa->minref = 1; - int EXTF_COMPOSITE = 0x1; - rfa->flags = EXTF_COMPOSITE; - m->m_ext.ext_refflags = (struct ext_ref*)rfa; - } else { - m->m_flags = M_PKTHDR; - m->m_data = m->m_pktdat; - } - - if (data) memcpy(m->m_data, data, size); - m->m_len = size; - if (is_header) { - m->m_flags |= M_PKTHDR; - m->m_pkthdr.len = m->m_len; - m->m_pkthdr.redzone = ((u_int32_t)(uintptr_t)m); - m->m_pkthdr.pkt_flags = pktflags; - } - - assert(m->m_len <= ((njcl > 0) ? njclbytes : MBIGCLBYTES)); - - return m; -} - -void* mcache_alloc(mcache_t* cp, int flags) { - if (cp->cluster_cache) { - return mbuf_create(NULL, cp->bufsize, false, cp->cluster_cache, MT_FREE, 0); - } - - void* m = NULL; - if (posix_memalign((void**)&m, MSIZE, cp->bufsize)) { - return NULL; - } - return m; -} - -uint16_t m_decref(struct mbuf* m) { - struct ext_ref* rfa = m->m_ext.ext_refflags; - if (rfa) { - rfa->refcnt--; - return rfa->refcnt; - } - return 0; -} - -struct mbuf* m_free(struct mbuf* m) { - if ((m->m_flags & M_EXT)) { - uint16_t refcnt = m_decref(m); - if (!refcnt) { - free(m->m_ext.ext_buf); - free(m->m_ext.ext_refflags); - } - } - struct mbuf* ret = m->m_next; - free(m); - return ret; -} - -void mcache_free(mcache_t* cp, void* buf) { - if (cp->cluster_cache) { - m_free((struct mbuf*)buf); - } else { - free(buf); - } -} - -void mcache_init() { - // Nothing to do for mocked case. -} - -void mcache_reap_now() { assert(false); } - -int mcache_alloc_ext(mcache_t* cp, void** list, unsigned int num, int wait) { - struct mbuf* m = (struct mbuf*)calloc(1, sizeof(struct mbuf)); - m->m_hdr.mh_next = NULL; - m->m_hdr.mh_nextpkt = NULL; - m->m_type = MT_FREE; - m->m_flags = M_EXT; - m->m_hdr.mh_data = (caddr_t)calloc(1, num * cp->bufsize); - m->m_hdr.mh_len = num * cp->bufsize; - *list = m; - return 1; -} - -void mcache_audit_cache() { assert(false); } - -void mcache_audit_free_verify() { assert(false); } - -void mcache_audit_free_verify_set() { assert(false); } - -void mcache_bkt_isempty() { assert(false); } - -void mcache_buffer_log() { assert(false); } - -unsigned int mcache_cache_line_size() { - return 64; -} - -void mcache_dump_mca() { assert(false); } - -void mcache_free_ext() { assert(false); } - -unsigned int mcache_getflags() { return 0; } - -void mcache_purge_cache() { assert(false); } - -void mcache_reap() { assert(false); } - -void mcache_set_pattern() { assert(false); } - -void mcache_waiter_dec() { assert(false); } - -void mcache_waiter_inc() { assert(false); } \ No newline at end of file diff --git a/fuzz/fakes/osfmk_stubs.c b/fuzz/fakes/osfmk_stubs.c deleted file mode 100644 index 8f673cb..0000000 --- a/fuzz/fakes/osfmk_stubs.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include - -void kheap_startup_init() {} - -void zone_view_startup_init() {} - -void lck_attr_startup_init(struct lck_attr_startup_spec *sp) {} - -void lck_grp_startup_init() {} - -lck_attr_t *lck_attr_alloc_init() { return (void *)1; } - -void lck_mtx_assert() {} - -void lck_mtx_init() {} - -void lck_mtx_lock() {} - -void lck_spin_init() {} - -void lck_rw_startup_init(struct lck_rw_startup_spec *spec) {} - -// fake these so they aren't null but still invalid -lck_grp_attr_t *lck_grp_attr_alloc_init( -void) { - return (void*)1; -} - -lck_grp_t *lck_grp_alloc_init( - const char* grp_name, - lck_grp_attr_t *attr) { - return (void*)1; - } - -lck_rw_t *lck_rw_alloc_init( - lck_grp_t *grp, - lck_attr_t *attr) { return (void*)1; } - -lck_mtx_t *lck_mtx_alloc_init( - lck_grp_t *grp, - lck_attr_t *attr) { return (void*)1; } - -lck_spin_t *lck_spin_alloc_init( - lck_grp_t *grp, - lck_attr_t *attr) { return (void*)1; } - -void lck_mtx_lock_spin() {} - -void lck_mtx_convert_spin() {} - -void lck_mtx_free() {} - -void lck_rw_init() {} - -void lck_mtx_unlock() {} - -void lck_attr_free() {} - -void lck_attr_setdebug() {} - -OS_OVERLOADABLE -uint64_t counter_load(unsigned long long **counter) { assert(false); } - -int32_t sysctl_get_bound_cpuid(void) { assert(false); } - -kern_return_t sysctl_thread_bind_cpuid(int32_t cpuid) { assert(false); } - -kern_return_t kernel_memory_allocate(vm_map_t map, vm_offset_t *addrp, - vm_size_t size, vm_offset_t mask, - kma_flags_t flags, vm_tag_t tag) { - assert(false); -} - -void lck_mtx_startup_init(struct lck_mtx_startup_spec *spec) {} - -void -btlog_add_entry(btlog_t *btlog, - void *element, - uint8_t operation, - void *bt[], - size_t btcount) -{} - -void -btlog_remove_entries_for_element(btlog_t *btlog, - void *element) {} - -btlog_t * -btlog_create(size_t numrecords, - size_t record_btdepth, - boolean_t caller_will_remove_entries_for_element) { - assert(false); - } - -void machine_init(void) { assert(false); } - -void device_service_create(void) { assert(false); } - -void bsd_init(void) { assert(false); } - -void slave_machine_init(__unused void *param) { assert(false); } - -void phys_carveout_init() { assert(false); } -void hv_support_init() { assert(false); } -void vm_mem_bootstrap() { assert(false); } -void kdp_init() { assert(false); } -void workq_init() { assert(false); } -void machine_lockdown() { assert(false); } -void thread_max() { assert(false); } -void kperf_init_early() { assert(false); } -void thread_daemon_init() { assert(false); } -void mac_policy_initmach() { assert(false); } -void vm_kernel_reserved_entry_init() { assert(false); } -void kdebug_free_early_buf() { assert(false); } -void turnstiles_init() { assert(false); } -void vm_commpage_text_init() { assert(false); } -void machine_load_context() { assert(false); } -void ipc_pthread_priority_init() { assert(false); } -void PE_i_can_has_debugger() { assert(false); } -void sfi_init() { assert(false); } -void dtrace_early_init() { assert(false); } -void sched_startup() { assert(false); } -void ml_get_interrupts_enabled() { assert(false); } -void kernel_list_tests() { assert(false); } -void thread_machine_init_template() { assert(false); } -void task_max() { assert(false); } -void thread_get_perfcontrol_class() { assert(false); } -void ml_set_interrupts_enabled() { assert(false); } -void processor_up() { assert(false); } -void exception_init() { assert(false); } -void vm_set_restrictions() { assert(false); } -void thread_init() { assert(false); } -void console_init() { assert(false); } -void idle_thread_create() { assert(false); } -void PE_init_iokit() { assert(false); } -void mac_policy_init() { assert(false); } -void bsd_scale_setup() { assert(false); } -void mapping_adjust() { assert(false); } -void trust_cache_init() { assert(false); } - -vm_size_t mem_size = 4000000; - -void version_minor() { assert(false); } -void restartable_init() { assert(false); } -void clock_init() { assert(false); } -void kpc_init() { assert(false); } -void vnguard_policy_init() { assert(false); } -void coalitions_init() { assert(false); } -void PE_lockdown_iokit() { assert(false); } -void work_interval_subsystem_init() { assert(false); } -void kernel_do_post() { assert(false); } -void stack_alloc_try() { assert(false); } -void vm_commpage_init() { assert(false); } -void serial_keyboard_init() { assert(false); } -void stackshot_init() { assert(false); } -void task_threadmax() { assert(false); } -void version() { assert(false); } -void mach_init_activity_id() { assert(false); } -void current_processor() { assert(false); } -void telemetry_init() { assert(false); } -void vm_pageout() { assert(false); } -void sdt_early_init() { assert(false); } -void task_init() { assert(false); } -void vm_page_init_local_q() { assert(false); } -void bootprofile_init() { assert(false); } - -struct machine_info machine_info; -uint64_t max_mem_actual = 4000000; - -void thread_bind() { assert(false); } -void spinlock_timeout_panic() { assert(false); } -void sched_dualq_dispatch() { assert(false); } -void processor_state_update_explicit() { assert(false); } -void mapping_free_prime() { assert(false); } -void PE_parse_boot_arg_str() { assert(false); } -void timer_start() { assert(false); } -void atm_init() { assert(false); } -void version_major() { assert(false); } -void waitq_bootstrap() { assert(false); } -void sched_init() { assert(false); } -void kperf_init() { assert(false); } -void kernel_thread_create() { assert(false); } -void machine_set_current_thread() { assert(false); } -void idle_thread() { assert(false); } -void kasan_late_init() { assert(false); } -void thread_call_initialize() { assert(false); } -void clock_service_create() { assert(false); } -void ipc_thread_call_init() { assert(false); } -void corpses_init() { assert(false); } -void OSKextRemoveKextBootstrap() { assert(false); } -void bank_init() { assert(false); } -void kdebug_init() { assert(false); } -void vm_free_delayed_pages() { assert(false); } -void initialize_screen() { assert(false); } -void serverperfmode() { assert(false); } -void host_statistics_init() { assert(false); } - -boolean_t doprnt_hide_pointers = true; diff --git a/fuzz/fakes/stubs.c b/fuzz/fakes/stubs.c deleted file mode 100644 index 2a93ef0..0000000 --- a/fuzz/fakes/stubs.c +++ /dev/null @@ -1,912 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -// Unimplemented stub functions -// These are placeholders that will be replaced with real or fake implementations -// when the fuzzed code attempts to use them. - -#include -#include -#include -#include - -int printf(const char* format, ...); - -__attribute__((visibility("default"))) -void Assert(const char* file, int line, const char* expression) { - printf("%s: assert failed on line %d: %s\n", file, line, expression); - __builtin_trap(); -} - -void IOBSDGetPlatformUUID() { assert(false); } - -void IOMapperInsertPage() { assert(false); } - -void IOPMCopySleepWakeUUIDKey() { assert(false); } - -void IOTaskHasEntitlement() { assert(false); } - -void OSMalloc_Tagalloc() { assert(false); } - -void OSMalloc_Tagfree() { assert(false); } - -void act_set_astbsd() { assert(false); } - -void act_set_astkevent() { assert(false); } - -void addupc_task() { assert(false); } - -void assert_wait() { assert(false); } - -void audit_arg_addr() { assert(false); } - -void audit_arg_cmd() { assert(false); } - -void audit_arg_ctlname() { assert(false); } - -void audit_arg_fd() { assert(false); } - -void audit_arg_file() { assert(false); } - -void audit_arg_pid() { assert(false); } - -void audit_arg_process() { assert(false); } - -void audit_arg_signum() { assert(false); } - -void audit_arg_value64() { assert(false); } - -void audit_syscalls() { assert(false); } - -void bsd_exception() { assert(false); } - -void bsd_timeout() { assert(false); } - -void bsdinit_task() { assert(false); } - -void cc_rand_generate() { assert(false); } - -void check_actforsig() { assert(false); } - -void clear_thread_rwlock_boost() { assert(false); } - -void clock_absolutetime_interval_to_deadline() { assert(false); } - -void clock_continuoustime_interval_to_deadline() { assert(false); } - -void clock_deadline_for_periodic_event() { assert(false); } - -void clock_get_calendar_microtime() { assert(false); } - -void clock_get_calendar_nanotime() { assert(false); } - -void clock_get_uptime() { assert(false); } - -void coalition_get_leader() { assert(false); } - -void coalition_is_leader() { assert(false); } - -void copyin_word() { assert(false); } - -void copyinstr() { assert(false); } - -void copypv() { assert(false); } - -void copywithin() { assert(false); } - -void coredump() { assert(false); } - -void cs_identity_get() { assert(false); } - -void current_task() { assert(false); } - -void deflate() { assert(false); } - -void deflateReset() { assert(false); } - -void enodev() { assert(false); } - -void enodev_strat() { assert(false); } - -void exit_with_reason() { assert(false); } - -void fs_filtops() { assert(false); } - -void fsevent_filtops() { assert(false); } - -void fuulong() { assert(false); } - -void gPEClockFrequencyInfo() { assert(false); } - -void* g_crypto_funcs = NULL; - -void get_bsdtask_info() { assert(false); } - -void get_bsdthreadtask_info() { assert(false); } - -void get_signalact() { assert(false); } - -void get_threadtask() { assert(false); } - -void get_useraddr() { assert(false); } - -void hashaddr() { assert(false); } - -void hashbacktrace() { assert(false); } - -void hostname() { assert(false); } - -void hz() { assert(false); } - -void inflate() { assert(false); } - -void inflateReset() { assert(false); } - -void initproc() { assert(false); } - -void ipc_entry_name_mask() { assert(false); } - -void is_kerneltask() { assert(false); } - -void itimerdecr() { assert(false); } - -void itimerfix() { assert(false); } - -void kauth_authorize_generic() { assert(false); } - -void kauth_cred_getgid() { assert(false); } - -void kauth_cred_getruid() { assert(false); } - -void kauth_cred_getsvuid() { assert(false); } - -void kauth_getruid() { assert(false); } - -void kcdata_estimate_required_buffer_size() { assert(false); } - -void kcdata_get_memory_addr() { assert(false); } - -void kcdata_memcpy() { assert(false); } - -void kcdata_memory_static_init() { assert(false); } - -void kdp_get_interface() { assert(false); } - -void kdp_is_in_zone() { assert(false); } - -void kdp_set_gateway_mac() { assert(false); } - -void kdp_set_ip_and_mac_addresses() { assert(false); } - -void kernel_debug_filtered() { assert(false); } - -void kernel_task() { assert(false); } - -void launchd_exit_reason_get_string_desc() { assert(false); } - -void lck_mtx_lock_spin_always() { assert(false); } - -void lck_rw_destroy() { assert(false); } - -void lck_rw_lock_exclusive_to_shared() { assert(false); } - -void lck_rw_lock_shared_to_exclusive() { assert(false); } - -void lck_rw_sleep() { assert(false); } - -void lck_spin_assert() { assert(false); } - -void lck_spin_destroy() { assert(false); } - -void ledger_get_task_entry_info_multiple() { assert(false); } - -void ledger_info() { assert(false); } - -void ledger_template_info() { assert(false); } - -void mac_error_select() { assert(false); } - -void mac_policy_list() { assert(false); } - -void mac_policy_list_conditional_busy() { assert(false); } - -void mac_policy_list_unbusy() { assert(false); } - -void mac_proc_check_ledger() { assert(false); } - -void mac_proc_check_signal() { assert(false); } - -void mac_socket_check_received() { assert(false); } - -void mac_socket_check_stat() { assert(false); } - -void mac_system_enforce() { assert(false); } - -void mach_absolutetime_asleep() { assert(false); } - -void machport_filtops() { assert(false); } - -void max_mem() { assert(false); } - -void mb_map() { assert(false); } - -void memorystatus_filtops() { assert(false); } - -void memorystatus_kevent_init() { assert(false); } - -void msleep() { assert(false); } - -void msleep0() { assert(false); } - -void msleep1() { assert(false); } - -void nanoseconds_to_absolutetime() { assert(false); } - -void nanotime() { assert(false); } - -void pg_rele() { assert(false); } - -void pgfind() { assert(false); } - -void pgrp_iterate() { assert(false); } - -void port_name_to_thread() { assert(false); } - -void proc_get_effective_task_policy() { assert(false); } - -void proc_getcdhash() { assert(false); } - -void proc_iterate() { assert(false); } - -void proc_klist_lock() { assert(false); } - -void proc_klist_unlock() { assert(false); } - -void proc_knote() { assert(false); } - -void proc_list_lock() { assert(false); } - -void proc_list_unlock() { assert(false); } - -void proc_lock() { assert(false); } - -void proc_log_32bit_telemetry() { assert(false); } - -void proc_name_address() { assert(false); } - -void proc_parentdropref() { assert(false); } - -void proc_parentholdref() { assert(false); } - -void proc_pgrp() { assert(false); } - -void proc_self() { assert(false); } - -void proc_set_thread_policy() { assert(false); } - -void proc_signal() { assert(false); } - -void proc_spinlock() { assert(false); } - -void proc_spinunlock() { assert(false); } - -void proc_unlock() { assert(false); } - -void proc_uuid_policy_kernel() { assert(false); } - -void proc_uuid_policy_lookup() { assert(false); } - -void pthread_functions() { assert(false); } - -void pthread_priority_canonicalize() { assert(false); } - -void ptmx_kqops() { assert(false); } - -void ptsd_kqops() { assert(false); } - -void pzfind() { assert(false); } - -void sane_size() { assert(false); } - -void securelevel() { assert(false); } - -void semaphore_timedwait_signal_trap_internal() { assert(false); } - -void semaphore_timedwait_trap_internal() { assert(false); } - -void semaphore_wait_signal_trap_internal() { assert(false); } - -void semaphore_wait_trap_internal() { assert(false); } - -void sendsig() { assert(false); } - -void set_thread_rwlock_boost() { assert(false); } - -void spec_filtops() { assert(false); } - -void subyte() { assert(false); } - -void suulong() { assert(false); } - -void sysctl__debug_children() { assert(false); } - -void sysctl__kern_children() { assert(false); } - -void sysctl__net_children() { assert(false); } - -void sysctl__net_link_generic_system_children() { assert(false); } - -void sysctl__sysctl_children() { assert(false); } - -void task_consume_32bit_log_flag() { assert(false); } - -void task_deallocate() { assert(false); } - -void task_did_exec() { assert(false); } - -void task_hold() { assert(false); } - -void task_is_active() { assert(false); } - -void task_is_exec_copy() { assert(false); } - -void task_is_halting() { assert(false); } - -void task_release() { assert(false); } - -void task_resume_internal() { assert(false); } - -void task_suspend_internal() { assert(false); } - -void task_vtimer_clear() { assert(false); } - -void task_vtimer_set() { assert(false); } - -void task_vtimer_update() { assert(false); } - -void task_wait() { assert(false); } - -void telemetry_timer_event() { assert(false); } - -void thread_add_ipc_override() { assert(false); } - -void thread_add_sync_ipc_override() { assert(false); } - -void thread_block() { assert(false); } - -void thread_block_parameter() { assert(false); } - -void thread_call_cancel() { assert(false); } - -void thread_call_cancel_wait() { assert(false); } - -void thread_call_enter() { assert(false); } - -void thread_call_free() { assert(false); } - -void thread_call_func_cancel() { assert(false); } - -void thread_call_func_delayed() { assert(false); } - -void thread_call_isactive() { assert(false); } - -void thread_drop_ipc_override() { assert(false); } - -void thread_drop_sync_ipc_override() { assert(false); } - -void thread_ends_owning_workloop() { assert(false); } - -void thread_get_ipc_override() { assert(false); } - -void thread_get_tag() { assert(false); } - -void thread_handoff() { assert(false); } - -void thread_owned_workloops_count() { assert(false); } - -void thread_qos_from_pthread_priority() { assert(false); } - -void thread_reference() { assert(false); } - -void thread_rettokern_addr() { assert(false); } - -void thread_set_pending_block_hint() { assert(false); } - -void thread_set_thread_name() { assert(false); } - -void thread_set_voucher_name() { assert(false); } - -void thread_should_abort() { assert(false); } - -void thread_should_halt() { assert(false); } - -void thread_starts_owning_workloop() { assert(false); } - -void thread_tid() { assert(false); } - -void thread_update_ipc_override() { assert(false); } - -void thread_wakeup_thread() { assert(false); } - -void tick() { assert(false); } - -void timeout_with_leeway() { assert(false); } - -void timespec_is_valid() { assert(false); } - -void tsleep() { assert(false); } - -void tsleep0() { assert(false); } - -void tsleep1() { assert(false); } - -void tstoabstime() { assert(false); } - -void tty_filtops() { assert(false); } - -void tty_pgrp() { assert(false); } - -void tvtoabstime() { assert(false); } - -void unix_syscall_return() { assert(false); } - -void untimeout() { assert(false); } - -void vaddlog() { assert(false); } - -void vfs_context_create() { assert(false); } - -void vfs_context_rele() { assert(false); } - -void vm_kernel_slid_base() { assert(false); } - -void vm_kernel_slid_top() { assert(false); } - -void vm_kernel_slide() { assert(false); } - -void vn_stat() { assert(false); } - -void vnode_filtops() { assert(false); } - -void vnode_isfifo() { assert(false); } - -void waitq_assert_wait64() { assert(false); } - -void waitq_assert_wait64_leeway() { assert(false); } - -void waitq_clear_prepost() { assert(false); } - -void waitq_deinit() { assert(false); } - -void waitq_get_prepost_id() { assert(false); } - -void waitq_init() { assert(false); } - -void waitq_is_valid() { assert(false); } - -void waitq_link() { assert(false); } - -void waitq_link_release() { assert(false); } - -void waitq_link_reserve() { assert(false); } - -void waitq_set_alloc() { assert(false); } - -void waitq_set_clear_preposts() { assert(false); } - -void waitq_set_deinit() { assert(false); } - -void waitq_set_init() { assert(false); } - -void waitq_set_is_valid() { assert(false); } - -void waitq_set_unlink_all() { assert(false); } - -void waitq_unlink() { assert(false); } - -void waitq_unlink_by_prepost_id() { assert(false); } - -void waitq_wakeup64_all() { assert(false); } - -void waitq_wakeup64_one() { assert(false); } - -void wqset_id() { assert(false); } - -void wqset_waitq() { assert(false); } - -void zalloc_canblock() { assert(false); } - -void zfill() { assert(false); } - -void kernel_pmap() { assert(false); } - -void kmem_free() { assert(false); } - -void cru2x() { assert(false); } - -void mac_vnode_check_create() { assert(false); } - -void mac_vnode_check_uipc_bind() { assert(false); } - -void mac_vnode_check_uipc_connect() { assert(false); } - -void namei() { assert(false); } - -void nameidone() { assert(false); } - -void vfs_context_ucred() { assert(false); } - -void vn_create() { assert(false); } - -void vnode_authorize() { assert(false); } - -void vnode_put() { assert(false); } - -void vnode_ref() { assert(false); } - -void vnode_rele() { assert(false); } - -void audit_arg_sockaddr() { assert(false); } - -void audit_arg_socket() { assert(false); } - -void audit_arg_value32() { assert(false); } - -void vfs_context_cwd() { assert(false); } - -void vnode_isreg() { assert(false); } - -void vnode_size() { assert(false); } - -void aes_decrypt_aad_gcm() { assert(false); } - -void aes_decrypt_cbc() { assert(false); } - -void aes_decrypt_finalize_gcm() { assert(false); } - -void aes_decrypt_gcm() { assert(false); } - -void aes_decrypt_get_ctx_size_gcm() { assert(false); } - -void aes_decrypt_key() { assert(false); } - -void aes_decrypt_key_gcm() { assert(false); } - -void aes_decrypt_set_iv_gcm() { assert(false); } - -void aes_encrypt_aad_gcm() { assert(false); } - -void aes_encrypt_cbc() { assert(false); } - -void aes_encrypt_finalize_gcm() { assert(false); } - -void aes_encrypt_gcm() { assert(false); } - -void aes_encrypt_get_ctx_size_gcm() { assert(false); } - -void aes_encrypt_inc_iv_gcm() { assert(false); } - -void aes_encrypt_key() { assert(false); } - -void aes_encrypt_key_with_iv_gcm() { assert(false); } - -void aes_encrypt_reset_gcm() { assert(false); } - -void clock_get_system_microtime() { assert(false); } - -void thread_call_enter1_delayed() { assert(false); } - -void panic() { assert(false); } - -struct os_log_s { - int a; -}; - -struct os_log_s _os_log_default; - -uint32_t net_flowhash_mh3_x86_32(const void* key, uint32_t len, - const uint32_t seed) { - assert(false); - return 1; -} - -void cc_clear() { assert(false); } - -void cc_cmp_safe() { assert(false); } - -void getsectdatafromheader() { assert(false); } - -void _mh_execute_header() { assert(false); } - -void net_flowhash() { assert(false); } - -void os_cpu_in_cksum() { assert(false); } - -void os_cpu_in_cksum_mbuf() { assert(false); } - -void proc_name() { assert(false); } - -void thread_terminate() { assert(false); } - -void uuid_generate() { assert(false); } - -void uuid_parse() { assert(false); } - -void _pthread_priority_normalize() { assert(false); } - -void workq_kern_threadreq_modify() { assert(false); } - -void nat464_translate_proto() { assert(false); } - -void turnstile_cleanup() { assert(false); } - -void thread_wakeup_prim() { assert(false); } - -void waitq_wakeup64_thread() { assert(false); } - -void turnstile_prepare() { assert(false); } - -void assert_wait_deadline() { assert(false); } - -void nat464_synthesize_ipv4() { assert(false); } - -void clat_debug() { assert(false); } - -void workq_kern_threadreq_redrive() { assert(false); } - -void zdestroy() { assert(false); } - -void turnstile_update_inheritor() { assert(false); } - -void thread_handoff_parameter() { assert(false); } - -void in6_clat46_eventhdlr_callback() { assert(false); } - -void turnstile_update_inheritor_complete() { assert(false); } - -void nat464_insert_frag46() { assert(false); } - -void nat464_synthesize_ipv6() { assert(false); } - -void turnstile_complete() { assert(false); } - -void nat464_translate_64() { assert(false); } - -void waitq_set_should_lazy_init_link() { assert(false); } - -void workq_is_exiting() { assert(false); } - -void sysctl_helper_waitq_set_nelem() { assert(false); } - -void turnstile_alloc() { assert(false); } - -void workq_kern_threadreq_update_inheritor() { assert(false); } - -void thread_handoff_deallocate() { assert(false); } - -void workq_kern_threadreq_unlock() { assert(false); } - -void telemetry_pmi_setup() { assert(false); } - -void nat464_translate_46() { assert(false); } - -void waitq_set_lazy_init_link() { assert(false); } - -void workq_thread_set_max_qos() { assert(false); } - -void workq_kern_threadreq_lock() { assert(false); } - -void nat464_cksum_fixup() { assert(false); } - -void turnstile_deallocate() { assert(false); } - -void in6_clat46_event_enqueue_nwk_wq_entry() { assert(false); } - -void workq_kern_threadreq_initiate() { assert(false); } - -void turnstile_reference() { assert(false); } - -void _pthread_priority_combine() { assert(false); } - -void cchmac_final() { assert(false); } - -void thread_update_kevent_override() { assert(false); } - -void thread_add_kevent_override() { assert(false); } - -void _disable_preemption() { assert(false); } - -void lck_spin_sleep_with_inheritor() { assert(false); } - -void ccsha256_di() { assert(false); } - -void copysize_limit_panic() { assert(false); } - -void sysctl_load_devicetree_entries() { assert(false); } - -void mpsc_test_pingpong() { assert(false); } - -void sysctl_task_get_no_smt() { assert(false); } - -void hostname_lock() { assert(false); } - -void cchmac_update() { assert(false); } - -void _os_log_internal_driverKit() { assert(false); } - -void machine_tecs() { assert(false); } - -void machine_csv() { assert(false); } - -void act_clear_astkevent() { assert(false); } - -void cchmac_init() { assert(false); } - -void thread_drop_servicer_override() { assert(false); } - -void thread_update_servicer_override() { assert(false); } - -void wakeup_one_with_inheritor() { assert(false); } - -void sysctl__machdep_children() { assert(false); } - -void thread_unfreeze_base_pri() { assert(false); } - -void turnstile_deallocate_safe() { assert(false); } - -void task_exc_guard_default() { assert(false); } - -void sysctl_task_set_no_smt() { assert(false); } - -void current_uthread() { assert(false); } - -void filt_wldetach_sync_ipc() { assert(false); } - -void _enable_preemption() { assert(false); } - -void restricted_port_bitmap() { assert(false); } - -void cfil_crypto_sign_data() { assert(false); } - -void thread_set_no_smt() { assert(false); } - -void net_mpklog_enabled() { assert(false); } - -void cfil_crypto_init_client() { assert(false); } - -void task_info() { assert(false); } - -void thread_get_no_smt() { assert(false); } - -void task_get_coalition() { assert(false); } - -void atm_get_diagnostic_config() { assert(false); } - -void cfil_crypto_cleanup_state() { assert(false); } - -void copyin_atomic64() { assert(false); } - -void thread_drop_kevent_override() { assert(false); } - -void filt_wlattach_sync_ipc() { assert(false); } - -void thread_deallocate_safe() { assert(false); } - -void vm_kernel_addrhash() { assert(false); } - -void _vm_kernel_addrhash_XNU_INTERNAL() { assert(false); } - -void thread_add_servicer_override() { assert(false); } - -void net_mpklog_type() { assert(false); } - -void mach_bridge_remote_time() { assert(false); } - -void vn_getpath_ext() { assert(false); } - -void wakeup_all_with_inheritor() { assert(false); } - -void registerSleepWakeInterest() { assert(false); } - -void absolutetime_to_microtime() { assert(false); } - -void thread_abort() { - assert(false); -} - -void strnstr() { - assert(false); -} - -void thread_abort_safely() { - assert(false); -} - -uint32_t crc32(uint32_t crc, const void *buf, size_t size) { - assert(false); - return 0; -} - -void cs_get_cdhash() { - assert(false); -} - -void cs_hash_type() { - assert(false); -} - -void cs_valid() { - assert(false); -} - -void mac_file_notify_close() { - assert(false); -} - -void mach_bridge_timer_enable() { - assert(false); -} - -void machine_thread_function_pointers_convert_from_user() { - assert(false); -} - -uint64_t mem_actual = 0x41414141; - -void proc_min_sdk() { - assert(false); -} - -void proc_platform() { - assert(false); -} - -void proc_sdk() { - assert(false); -} - -const char *sysctl_debug_get_preoslog(size_t *size) { - assert(false); - return NULL; -} - -void task_get_filter_msg_flag() { - assert(false); -} - -void task_set_filter_msg_flag() { - assert(false); -} - -void thread_zone() { - assert(false); -} - -void zone_require() { - assert(false); -} diff --git a/fuzz/fakes/zalloc.c b/fuzz/fakes/zalloc.c deleted file mode 100644 index 687d64f..0000000 --- a/fuzz/fakes/zalloc.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include - -#include "osfmk/kern/kalloc.h" -#include "osfmk/kern/zalloc_internal.h" -#include "osfmk/mach/i386/kern_return.h" -#include "BUILD/obj/EXPORT_HDRS/osfmk/kern/zalloc.h" - -int printf(const char*, ...) __printflike(1, 2); - -void free(void *ptr); - -// We link these in from libc/asan -void* malloc(size_t size); -void* calloc(size_t nmemb, size_t size); -void free(void* ptr); -int posix_memalign(void** memptr, size_t alignment, size_t size); - -struct zone* zinit(uintptr_t size, uintptr_t max, uintptr_t alloc, - const char* name) { - struct zone* zone = (struct zone*)calloc(1, sizeof(struct zone)); - zone->z_elem_size = size; - return zone; -} - -zone_t zone_create( - const char *name, - vm_size_t size, - zone_create_flags_t flags) -{ - struct zone* zone = (struct zone*)calloc(1, sizeof(struct zone)); - zone->z_elem_size = size; - return zone; -} - -// TODO: validation here -void zone_change() { return; } - -void* zalloc(struct zone* zone) { - assert(zone != NULL); - return calloc(1, zone->z_elem_size); -} - -void* zalloc_noblock(struct zone* zone) { return zalloc(zone); } - -extern void zfree( - zone_or_view_t zone_or_view, - void *elem) { - (void)zone_or_view; - free(elem); -} - -int cpu_number() { return 0; } - -void* kalloc_canblock(size_t* psize, bool canblock, void* site) { - return malloc(*psize); -} - -static bool mb_is_ready = false; -extern unsigned char* mbutl; -extern unsigned char* embutl; -static size_t current_page = 0; - -uintptr_t kmem_mb_alloc(unsigned int mbmap, int size, int physContig, - int* err) { - *err = 0; - - if (!mb_is_ready) { - // 268 MB - *err = posix_memalign((void**)&mbutl, 4096, 4096 * 65535); - if (*err) { - return 0; - } - embutl = (unsigned char*)((uintptr_t)mbutl + (4096 * 65535)); - - mb_is_ready = true; - } - - assert(mbutl); - int pages = size / 4096; - uintptr_t ret = (uintptr_t)mbutl + (current_page * 4096); - current_page += pages; - - return ret; -} - -// TODO: actually simulate physical page mappings -unsigned int pmap_find_phys(int pmap, uintptr_t va) { return 0; } - -void* __MALLOC_ZONE(size_t size, int type, int flags, - vm_allocation_site_t* site) { - return malloc(size); -} - -void _FREE_ZONE(void* elem, size_t size, int type) { free(elem); } - -#undef kfree -void kfree(void* data, size_t size) { free(data); } - -void* realloc(void* ptr, size_t size); - -void* __REALLOC(void* addr, size_t size, int type, int flags, - vm_allocation_site_t* site) { - void* ptr = realloc(addr, size); - return ptr; -} - -void OSFree(void* ptr, uint32_t size, void* tag) { free(ptr); } - -void* OSMalloc(uint32_t size, void* tag) { - return malloc(size); -} - -SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_DATA_BUFFERS[1] = { - { - .kh_zones = NULL, - .kh_name = "data.", - .kh_heap_id = KHEAP_ID_DATA_BUFFERS, - } -}; - -SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_DEFAULT[1] = { - { - .kh_zones = NULL, - .kh_name = "default.", - .kh_heap_id = KHEAP_ID_DEFAULT, - } -}; - -KALLOC_HEAP_DEFINE(KHEAP_TEMP, "temp allocations", KHEAP_ID_DEFAULT); - -ZONE_VIEW_DEFINE(ZV_NAMEI, "vfs.namei", KHEAP_ID_DATA_BUFFERS, 1024); - -void abort() { - __builtin_trap(); -} - -#undef kheap_free -extern void -kheap_free( - kalloc_heap_t heap, - void *data, - vm_size_t size) { - free(data); -} - -__startup_func -void -zone_create_startup(struct zone_create_startup_spec *spec) -{ - *spec->z_var = zone_create(spec->z_name, spec->z_size, - spec->z_flags); -} - -_Atomic uint32_t bt_init_flag = 0; - -struct kalloc_result -kalloc_ext( - kalloc_heap_t kheap, - vm_size_t req_size, - zalloc_flags_t flags, - vm_allocation_site_t *site) { - void *addr = malloc(req_size); - if (flags & Z_ZERO) { - bzero(addr, req_size); - } - return (struct kalloc_result){ .addr = addr, .size = req_size }; -} - -void *zalloc_flags(union zone_or_view zov, zalloc_flags_t flags) { - return zalloc(zov.zov_zone); -} - -#undef kheap_free_addr -void kheap_free_addr( - kalloc_heap_t heap, - void *addr) { - free(addr); -} - -void *zalloc_permanent(vm_size_t size, vm_offset_t mask) { - return malloc(size); -} diff --git a/fuzz/gen/generate_syscall_api.py b/fuzz/gen/generate_syscall_api.py deleted file mode 100644 index ee3aa96..0000000 --- a/fuzz/gen/generate_syscall_api.py +++ /dev/null @@ -1,646 +0,0 @@ -""" -Copyright 2021 Google LLC - -@APPLE_OSREFERENCE_LICENSE_HEADER_START@ - -This file contains Original Code and/or Modifications of Original Code -as defined in and that are subject to the Apple Public Source License -Version 2.0 (the 'License'). You may not use this file except in -compliance with the License. The rights granted to you under the License -may not be used to create, or enable the creation or redistribution of, -unlawful or unlicensed copies of an Apple operating system, or to -circumvent, violate, or enable the circumvention or violation of, any -terms of an Apple operating system software license agreement. - -Please obtain a copy of the License at -http://www.opensource.apple.com/apsl/ and read it before using this file. - -The Original Code and all software distributed under the License are -distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -Please see the License for the specific language governing rights and -limitations under the License. - -@APPLE_OSREFERENCE_LICENSE_HEADER_END@ -""" - -"""Generate structured fuzzer code from syscall master list - -This file autogenerates code to call syscalls to let us get us -much initial fuzz coverage as possible. It is designed to work -with hand-written syscall descriptions as well. - -TODO(nedwill): support other syscall types such as mach traps - -Goals: -1) generate protobuf file with a message containing all syscalls -2) generate syscall wrapper code to expose from libxnu -3) generate c code to call wrappers given autogen syscall message - -First, we will simply print this output to the terminal for manual -use. Later, we can make this integration automatic, even making it -work with cmake/ninja. -""" - -ALLOWLIST = frozenset([ - 'socket', - 'close', - 'listen', - 'accept', - 'connect', - 'bind', - 'connectx', - 'disconnectx', - 'ioctl', - 'ioctl_data', - 'setsockopt', - 'getsockopt', - 'necp_match_policy', - 'necp_open', - 'necp_client_action', - 'necp_session_open', - 'necp_session_action', - 'recvmsg', - 'sendmsg', - 'recvfrom', - 'getpeername', - 'getsockname', - 'sendto', - 'shutdown', - 'socketpair', - 'recvmsg_nocancel', - 'sendmsg_nocancel', - 'recvfrom_nocancel', - 'accept_nocancel', - 'connect_nocancel', - 'sendto_nocancel', - # TODO(nedwill): need to build vm_unix.c for this - # 'pid_shutdown_sockets', - 'peeloff', - 'socket_delegate', - 'recvmsg_x', - 'sendmsg_x', - 'pipe', - 'shutdown', -]) - -import string - - -class ProtobufType: - pass - - -class BufferProtobufType(ProtobufType): - - def __init__(self, name): - self.name = name - self.type = 'bytes' - - def __str__(self): - return 'bytes' - - -class ProtobufField: - - def __init__(self, type_): - self.type = type_ - - @property - def used(self): - return True - - -class Optional(ProtobufField): - - @property - def field_type(self): - return 'optional' - - -class Repeated(ProtobufField): - - @property - def field_type(self): - return 'repeated' - - -class Result(ProtobufField): - - @property - def used(self): - return False - - -# TODO(nedwill): we should be detecting optional/repeated automatically -# from the number of indirections -TYPE_MAPPING = { - 'int': - Optional('int32'), - 'char *': - Optional('string'), - 'char **': - Repeated('string'), - 'const char *': - Optional('string'), - 'struct mac *': - Optional('StructMac'), - 'pid_t': - Optional('Pid'), - 'uint32_t': - Optional('uint32'), - 'uint64_t': - Optional('uint64'), - 'socklen_t *': - Result('socklen_t'), - 'size_t': - Optional('uint64_t'), - 'uid_t': - Optional('Uid'), - 'struct timeval *': - Optional('StructTimeval'), - 'const struct timespec *': - Optional('StructTimespec'), - 'struct itimerval *': - Optional('StructItimer'), - 'mach_port_name_t': - Optional('MachPort'), - 'au_asid_t': - Optional('AuditSessionID'), - 'const guardid_t *': - Optional('Guardid'), - 'u_int': - Optional('uint32'), - 'int *': - Optional('int32'), - 'gid_t': - Optional('Group'), - 'gid_t *': - Optional('Group'), - 'uint64_t *': - Optional('uint64'), - 'const sa_endpoints_t *': - Optional('Endpoints'), - 'sae_associd_t': - Optional('SaeAssocID'), - 'unsigned int': - Optional('uint32'), - 'const struct iovec *': - Optional('IOV'), - 'struct iovec *': - Optional('IOV'), - 'size_t *': - Optional('uint64'), - 'sae_connid_t *': - Optional('SaeConnID'), - 'sae_connid_t': - Optional('SaeConnID'), - 'u_long': - Optional('uint64'), - 'long': - Optional('int64'), - 'struct attrlist *': - Optional('Attrlist'), - 'const struct fhandle *': - Optional('StructFhandle'), - 'fhandle_t *': - Optional('StructFhandle'), - 'struct statfs *': - Optional('StructStatfs'), - 'struct statfs64 *': - Optional('StructStatfs64'), - 'au_id_t *': - Optional('AuId'), - 'long *': - Optional('int64'), - 'off_t': - Optional('int64'), - 'off_t *': - Optional('int64'), - 'u_long *': - Optional('uint64'), - 'id_t': - Optional('Id'), - 'struct rlimit *': - Optional('StructRlimit'), - 'struct rusage *': - Optional('StructRusage'), - 'uid_t *': - Optional('Uid'), - 'struct timezone *': - Optional('StructTimezone'), - 'const struct kevent *': - Optional('StructKevent'), - 'struct kevent *': - Optional('StructKevent'), - 'const struct kevent64_s *': - Optional('StructKevent64'), - 'struct kevent64_s *': - Optional('StructKevent64'), - 'const struct kevent_qos_s *': - Optional('StructKeventQos'), - 'struct kevent_qos_s *': - Optional('StructKeventQos'), - 'struct eventreq *': - Optional('StructEventreq'), - 'struct msqid_ds *': - Optional('StructMsqidDs'), - 'key_t': - Optional('Key'), - 'uuid_t': - Optional('Uuid'), - 'struct necp_aggregate_result *': - Optional('StructNecpAggregateResult'), - 'struct net_qos_param *': - Optional('StructNetQosParam'), - 'struct timex *': - Optional('StructTimex'), - 'struct ntptimeval *': - Optional('StructNtptimeval'), - 'struct kpersona_info *': - Optional('StructKpersonaInfo'), - 'struct pollfd *': - Optional('StructPollfd'), - 'pid_t': - Optional('Pid'), - 'pid_t *': - Optional('Pid'), - 'const struct _posix_spawn_args_desc *': - Optional('StructPosixSpawnArgsDesc'), - 'u_int32_t': - Optional('uint32'), - 'u_int32_t *': - Optional('uint32'), - 'const struct sigset_t *': - Optional('StructSigset'), - 'int64_t': - Optional('int64'), - 'struct sockaddr *': - Optional('StructSockaddr'), - 'struct msghdr *': - Optional('StructMsghdr'), - 'struct msghdr_x *': - Optional('StructMsghdrx'), - 'struct fssearchblock *': - Optional('StructFssearchBlock'), - 'uint32_t *': - Optional('uint32_t'), - 'struct searchstate *': - Optional('StructSearchstate'), - 'sem_t *': - Optional('Semaphore'), - 'semun_t *': - Optional('SemaphoreUn'), - 'semun_t': - Optional('SemaphoreUn'), - 'struct sembuf *': - Optional('StructSembuf'), - 'struct sf_hdtr *': - Optional('StructSfHdtr'), - 'const struct shared_file_mapping_np *': - Optional('StructSharedFileMappingNp'), - 'uint64_t*': - Optional('uint64'), - 'struct shmid_ds *': - Optional('StructShmidDs'), - 'struct __sigaction *': - Optional('StructSigaction1'), - 'struct sigaction *': - Optional('StructSigaction2'), - 'struct sigaltstack *': - Optional('StructSigaltstack'), - 'struct sigvec *': - Optional('StructSigvec'), - 'struct ucontext *': - Optional('StructUcontext'), - 'sigset_t': - Optional('Sigset'), - 'user_addr_t *': - Optional('bytes'), - 'user_size_t': - Optional('uint64'), - 'idtype_t': - Optional('Idtype'), - 'siginfo_t *': - Optional('Siginfo'), - 'const struct shared_file_np *': - Optional('SharedFileNp'), - 'const struct shared_file_mapping_slide_np *': - Optional('SharedFileMappingSlideNp'), - 'mach_port_name_t *': - Optional('MachPortName'), - - # TODO(nedwill): for these cases we probably want to implement - # manually or at least send the (void*)1 pointer to let copyin - # read from the fuzzed input stream - 'caddr_t': - Optional('bytes'), - 'user_addr_t': - Optional('bytes'), - 'unsigned char *': - Optional('bytes'), - 'void *': - Optional('bytes'), - 'int32_t': - Optional('int32'), -} - - -class Argument: - - def __init__(self, name, type_): - self.name = name - self.type = type_ - - @property - def protobuf_type(self): - return TYPE_MAPPING[self.type] - - @staticmethod - def _decode_string(raw): - raw = raw.rstrip().replace('\t', ' ') - # Find name - for idx in range(len(raw) - 1, 0, -1): - if raw[idx] not in (string.ascii_letters + string.digits + '_'): - break - - type_, name = raw[:idx + 1], raw[idx + 1:] - return type_, name - - @classmethod - def from_string(cls, raw): - type_, name = cls._decode_string(raw) - return cls(name.strip(), type_.strip()) - - def to_string(self): - return '{} {}'.format(self.type, self.name) - - -class BufferArgument(Argument): - - def __init__(self, arg1, arg2): - self.arg1 = arg1 - self.arg2 = arg2 - - @property - def protobuf_type(self): - return Optional(BufferProtobufType(self.arg1.name)) - - @property - def name(self): - return self.arg1.name - - -class FlagsArgument(Argument): - - def __init__(self, syscall_message_name, argument_name): - self.syscall_message_name = syscall_message_name - self.name = argument_name - - @property - def protobuf_type(self): - return Optional(self.syscall_message_name + 'Flags') - - -def arguments_form_buffer(argument, next_argument): - if next_argument.name == argument.name + 'size': - return True - if next_argument.name == argument.name + '_size': - return True - if next_argument.name == argument.name + 'len': - return True - if next_argument.name in ('length', 'bufferSize', 'nbyte'): - return True - if next_argument.name == 'bufferSize': - return True - if (argument.name, next_argument.name) in ( - ('buffer', 'bufsize'), - ('useraddr', 'usersize'), - ('buffer', 'size'), - ('value', 'size'), - # TODO(nedwill): for kdebug_typefilter we're really receiving size bytes, - # not sending [addr, addr+size) - ('addr', 'size'), - ('addr', 'len'), - ('in_buffer', 'in_buffer_length'), - # TODO(nedwill): used to store results not send bytes - ('out_buffer', 'out_buffer_length'), - ): - return True - return False - - -class Syscall: - - def __init__(self, name, return_type, original_arguments, arguments): - self.name = name - self.return_type = return_type - self.original_arguments = original_arguments - self.arguments = arguments - - @staticmethod - def _get_message_name(name): - return ''.join([e.capitalize() for e in name.split('_') if e]) - - @property - def message_name(self): - return self._get_message_name(self.name) - - @property - def message(self): - output = 'message {} {{\n'.format(self.message_name) - for i, argument in enumerate(self.arguments): - if not argument.protobuf_type.used: - continue - output += ' {} {} {} = {};\n'.format(argument.protobuf_type.field_type, - argument.protobuf_type.type, - argument.name, i + 1) - output += '}' - return output - - @property - def wrapper_prototype(self): - args = self.original_arguments + [ - Argument('retval', self.return_type + '*') - ] - output = 'int {}({})'.format(self.name + '_wrapper', - ', '.join(arg.to_string() for arg in args)) - return output - - @property - def wrapper_impl(self): - output = '' - output += '__attribute__((visibility("default"))) {} {{\n'.format(self.wrapper_prototype) - output += ' struct {}_args uap = {{\n'.format(self.name) - for arg in self.original_arguments: - output += ' .{} = {},\n'.format(arg.name, arg.name) - output += ' };\n' - output += ' return {}(kernproc, &uap, retval);\n'.format(self.name) - output += '}\n' - return output - - @property - def wrapper_real(self): - output = '' - output += '__attribute__((visibility("default"))) {} {{\n'.format(self.wrapper_prototype) - args = [] - for arg in self.original_arguments: - args.append(arg.name) - output += ' return {}({});\n'.format(self.name, ', '.join(args)) - output += '}\n' - return output - - @classmethod - def from_function(cls, function): - return_type = function.split(b' ')[0].decode() - name = function.split(b'(')[0].split(b' ')[1].decode() - argument_substring = function.split(b'(')[1].split(b')')[0] - - original_arguments = [] - arguments = [] - - if argument_substring != b'void': - args = [arg.strip().decode() for arg in argument_substring.split(b',')] - idx = 0 - while idx < len(args): - argument = Argument.from_string(args[idx]) - original_arguments.append(argument) - - # Check for buffer case - if idx + 1 < len(args): - next_argument = Argument.from_string(args[idx + 1]) - # TODO(nedwill): this is definitely not the only case for this - if arguments_form_buffer(argument, next_argument): - original_arguments.append(next_argument) - argument = BufferArgument(argument, next_argument) - idx += 1 - - # Check for flags case - elif argument.name == 'flags': - # TODO(nedwill): assert argument.type.is_integer - assert argument.type in ('int', 'short', 'uint32_t', 'unsigned int', - 'uint64_t', 'u_int') - argument = FlagsArgument(cls._get_message_name(name), argument.name) - - # TODO(nedwill): remove this hack once we find all void* usage - elif (name, argument.name) in (('fmount', 'data'), - ('iopolicysys', 'arg'), ('kevent_id', - 'data_out')): - argument.type = 'user_addr_t' - - arguments.append(argument) - idx += 1 - - return cls(name, return_type, original_arguments, arguments) - - @classmethod - def from_line(cls, line): - if b'\t' not in line: - return None - if b'{' not in line: - return None - if b'}' not in line: - return None - function = line.split(b'}')[0].split(b'{')[1].strip() - return cls.from_function(function) - - -class SyscallTracker: - - def __init__(self, prefix=0, use_allowlist=False): - """If prefix is non-0, only first 0 of sorted""" - with open('bsd/kern/syscalls.master', 'rb') as fd: - data = fd.read() - syscalls = {} - for line in data.splitlines(): - try: - syscall = Syscall.from_line(line) - except: - print('failed to decode', line) - raise - if syscall is None: - continue - # We intentionally ignore duplicate syscalls (e.g. nosys) - if syscall.name in syscalls: - continue - if use_allowlist and syscall.name not in ALLOWLIST: - continue - syscalls[syscall.name] = syscall - self.syscalls = list( - sorted(syscalls.values(), key=lambda syscall: syscall.name)) - if prefix: - self.syscalls = self.syscalls[:prefix] - - def generate_protobuf(self): - output = 'message GeneratedCommands {\n' - output += ' oneof command {\n' - for i, syscall in enumerate(self.syscalls): - output += ' {} {} = {};\n'.format(syscall.message_name, syscall.name, - i + 1) - output += ' }\n' - output += '}\n\n' - - syscall_output = [] - for syscall in self.syscalls: - try: - syscall_output.append(syscall.message) - except: - print('bug in message generation for', syscall.name) - raise - output += '\n\n'.join(syscall_output) - - return output - - def generate_syscall_wrappers_header(self): - output = '#ifndef SYSCALL_WRAPPERS_H_\n' - output += '#define SYSCALL_WRAPPERS_H_\n\n' - output += '#include \n' - output += '#include \n' - output += 'typedef unsigned char uuid_t[16];\n' - output += """typedef u_int64_t user_addr_t; -typedef u_int32_t socklen_t; -typedef struct sa_endpoints { - unsigned int sae_srcif; /* optional source interface */ - const struct sockaddr *sae_srcaddr; /* optional source address */ - socklen_t sae_srcaddrlen; /* size of source address */ - const struct sockaddr *sae_dstaddr; /* destination address */ - socklen_t sae_dstaddrlen; /* size of destination address */ -} sa_endpoints_t; -typedef __uint32_t sae_associd_t; -typedef __uint32_t sae_connid_t;\n""" - output += '\n' - - for syscall in self.syscalls: - output += syscall.wrapper_prototype + ';\n' - - output += '\n#endif // SYSCALL_WRAPPERS_H_\n' - return output - - def generate_syscall_wrappers_impl(self): - output = '#include "fuzz/api/syscall_wrappers.h"\n\n' - syscall_impls = [] - for syscall in self.syscalls: - syscall_impls.append(syscall.wrapper_impl) - output += '\n'.join(syscall_impls) - return output - - def generate_syscall_wrappers_real(self): - output = '#include "fuzz/api/syscall_wrappers.h"\n\n' - syscall_impls = [] - for syscall in self.syscalls: - syscall_impls.append(syscall.wrapper_real) - output += '\n'.join(syscall_impls) - return output - - def generate_calling_code(self): - raise Unimplemented - - -if __name__ == '__main__': - tracker = SyscallTracker(use_allowlist=True) - print(tracker.generate_protobuf()) - print(tracker.generate_syscall_wrappers_header()) - print(tracker.generate_syscall_wrappers_impl()) - print(tracker.generate_syscall_wrappers_real()) diff --git a/fuzz/gen/generate_syscall_api_test.py b/fuzz/gen/generate_syscall_api_test.py deleted file mode 100644 index 7d9371e..0000000 --- a/fuzz/gen/generate_syscall_api_test.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -Copyright 2021 Google LLC - -@APPLE_OSREFERENCE_LICENSE_HEADER_START@ - -This file contains Original Code and/or Modifications of Original Code -as defined in and that are subject to the Apple Public Source License -Version 2.0 (the 'License'). You may not use this file except in -compliance with the License. The rights granted to you under the License -may not be used to create, or enable the creation or redistribution of, -unlawful or unlicensed copies of an Apple operating system, or to -circumvent, violate, or enable the circumvention or violation of, any -terms of an Apple operating system software license agreement. - -Please obtain a copy of the License at -http://www.opensource.apple.com/apsl/ and read it before using this file. - -The Original Code and all software distributed under the License are -distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -Please see the License for the specific language governing rights and -limitations under the License. - -@APPLE_OSREFERENCE_LICENSE_HEADER_END@ -""" - -"""Tests for generate_syscall_api.""" - -import generate_syscall_api -import unittest -from unittest.mock import call, patch - -WAIT4_LINE = (b'7\tAUE_WAIT4\tALL\t{ int wait4(int pid, user_addr_t status,' - b' int options, user_addr_t rusage) NO_SYSCALL_STUB; } ') - -# Number of tests to use for the hand-written output below -NUM_TESTS = 2 - -GENERATED_MESSAGE1 = """message DisableThreadsignal { - optional int32 value = 1; -}""" - -GENERATED_MESSAGE2 = """message MacExecve { - optional string fname = 1; - repeated string argp = 2; - repeated string envp = 3; - optional StructMac mac_p = 4; -}""" - -# int __disable_threadsignal(int value); -GENERATED_PROTOBUF = """message GeneratedCommands {{ - oneof command {{ - DisableThreadsignal __disable_threadsignal = 1; - MacExecve __mac_execve = 2; - }} -}} - -{} - -{}""".format(GENERATED_MESSAGE1, GENERATED_MESSAGE2) - -MAC_GET_PID_MESSAGE = """message MacGetPid { - optional Pid pid = 1; - optional StructMac mac_p = 2; -}""" - -MAC_GETFSSTAT_MESSAGE = """message MacGetfsstat { - optional bytes buf = 1; - optional bytes mac = 2; - optional MacGetfsstatFlags flags = 3; -}""" - -AUDIT_MESSAGE = """message Audit { - optional bytes record = 1; -}""" - -GENERATED_SYSCALL_HEADER = """#ifndef SYSCALL_WRAPPERS_H_ -#define SYSCALL_WRAPPERS_H_ - -#include -#include -typedef unsigned char uuid_t[16]; -typedef u_int64_t user_addr_t; -typedef u_int32_t socklen_t; -typedef struct sa_endpoints { - unsigned int sae_srcif; /* optional source interface */ - const struct sockaddr *sae_srcaddr; /* optional source address */ - socklen_t sae_srcaddrlen; /* size of source address */ - const struct sockaddr *sae_dstaddr; /* destination address */ - socklen_t sae_dstaddrlen; /* size of destination address */ -} sa_endpoints_t; -typedef __uint32_t sae_associd_t; -typedef __uint32_t sae_connid_t; - -int __disable_threadsignal_wrapper(int value, int* retval); -int __mac_execve_wrapper(char * fname, char ** argp, char ** envp, struct mac * mac_p, int* retval); - -#endif // SYSCALL_WRAPPERS_H_ -""" - -GENERATED_SYSCALL_IMPL = """#include "fuzz/syscall_wrappers.h" - -__attribute__((visibility("default"))) int __disable_threadsignal_wrapper(int value, int* retval) { - struct __disable_threadsignal_args uap = { - .value = value, - }; - return __disable_threadsignal(kernproc, &uap, retval); -} - -__attribute__((visibility("default"))) int __mac_execve_wrapper(char * fname, char ** argp, char ** envp, struct mac * mac_p, int* retval) { - struct __mac_execve_args uap = { - .fname = fname, - .argp = argp, - .envp = envp, - .mac_p = mac_p, - }; - return __mac_execve(kernproc, &uap, retval); -} -""" - -class GenerateFuzzerTest(unittest.TestCase): - """Test syscall code generation.""" - - @patch('generate_syscall_api.Argument') - def test_syscall_from_line(self, MockArgument): - syscall = generate_syscall_api.Syscall.from_line(WAIT4_LINE) - self.assertEqual(syscall.name, 'wait4') - self.assertEqual(syscall.return_type, 'int') - self.assertEqual(len(syscall.arguments), 4) - calls = [ - call.from_string('int pid'), - call.from_string('user_addr_t status'), - call.from_string('int options'), - call.from_string('user_addr_t rusage') - ] - MockArgument.assert_has_calls(calls, any_order=True) - - def test_syscall_nosys_from_line(self): - line = b'0\tAUE_NULL\tALL\t{ int nosys(void); } { indirect syscall }' - syscall = generate_syscall_api.Syscall.from_line(line) - self.assertEqual(syscall.name, 'nosys') - self.assertEqual(len(syscall.arguments), 0) - - def test_int_argument(self): - argument = generate_syscall_api.Argument.from_string('int pid') - self.assertEqual(argument.name, 'pid') - self.assertEqual(argument.type, 'int') - - def test_tab_in_argument(self): - argument = generate_syscall_api.Argument.from_string('socklen_t\t*anamelen') - self.assertEqual(argument.name, 'anamelen') - self.assertEqual(argument.type, 'socklen_t *') - - def test_syscall_message_name(self): - syscall = generate_syscall_api.Syscall('__disable_threadsignal', 'void', [], []) - self.assertEqual(syscall.message_name, 'DisableThreadsignal') - - def test_syscall_message(self): - syscall = generate_syscall_api.Syscall( - '__disable_threadsignal', 'void', - [generate_syscall_api.Argument('value', 'int')], - [generate_syscall_api.Argument('value', 'int')]) - self.assertEqual(syscall.message, GENERATED_MESSAGE1) - - def test_struct_pointer_argument(self): - argument = generate_syscall_api.Argument.from_string('struct msghdr *msg') - self.assertEqual(argument.name, 'msg') - # TODO(nedwill): make type class more refined - self.assertEqual(argument.type, 'struct msghdr *') - - def test_struct_pointer_argument_underscore(self): - argument = generate_syscall_api.Argument.from_string('struct mac *mac_p') - self.assertEqual(argument.name, 'mac_p') - self.assertEqual(argument.type, 'struct mac *') - - def test_mac_get_pid_message(self): - syscall = generate_syscall_api.Syscall.from_function( - b'int __mac_get_pid(pid_t pid, struct mac *mac_p);') - self.assertEqual(syscall.message, MAC_GET_PID_MESSAGE) - - def test_mac_getfsstat_message(self): - syscall = generate_syscall_api.Syscall.from_function( - b'int __mac_getfsstat(user_addr_t buf, int bufsize, user_addr_t mac, int macsize, int flags);' - ) - self.assertEqual(syscall.message, MAC_GETFSSTAT_MESSAGE) - - def test_mac_audit_message(self): - syscall = generate_syscall_api.Syscall.from_function( - b'int audit(void *record, int length);') - self.assertEqual(syscall.message, AUDIT_MESSAGE) - - def test_parsing_does_not_crash(self): - # Creating syscall tracker should not crash - generate_syscall_api.SyscallTracker() - - def test_generate_protobuf(self): - tracker = generate_syscall_api.SyscallTracker(NUM_TESTS) - self.assertEqual(tracker.generate_protobuf(), GENERATED_PROTOBUF) - - def test_generate_protobuf_does_not_crash(self): - generate_syscall_api.SyscallTracker(0).generate_protobuf() - - def test_generate_syscall_header(self): - tracker = generate_syscall_api.SyscallTracker(NUM_TESTS) - self.assertEqual(tracker.generate_syscall_wrappers_header(), - GENERATED_SYSCALL_HEADER) - - def test_generate_syscall_header_does_not_crash(self): - generate_syscall_api.SyscallTracker(0).generate_syscall_wrappers_header() - - def test_generate_syscall_impl(self): - self.maxDiff = None - tracker = generate_syscall_api.SyscallTracker(NUM_TESTS) - self.assertEqual(tracker.generate_syscall_wrappers_impl(), GENERATED_SYSCALL_IMPL) - -if __name__ == '__main__': - unittest.main() diff --git a/fuzz/handlers/BUILD.bazel b/fuzz/handlers/BUILD.bazel new file mode 100644 index 0000000..b839552 --- /dev/null +++ b/fuzz/handlers/BUILD.bazel @@ -0,0 +1,124 @@ +cc_library( + name = "bsd", + srcs = [ + "bsd.cc", + "necp.cc", + ], + hdrs = [ + "bsd_types.h", + ], + visibility = [ + "//tools/generate_syscall:__pkg__", + ], + deps = [ + "//fuzz/host:headers", + "//fuzz/proto:bsd_cc_proto", + "//fuzz/proto:bsd_syscalls_cc_proto", + "//fuzz/xnu/bsd:headers", + "@com_google_absl//absl/strings:str_format", + ], +) + +cc_library( + name = "mach_message", + srcs = [ + "mach_message.cc", + ], + hdrs = [ + "mach_message.h", + ], + # TODO(nedwill): host_test violates the XNU > Host > Target dependency rule + # We should fix the host tests to not depend on handlers. + visibility = [ + "//fuzz/host:__pkg__", + ], + deps = [ + "//fuzz/host", + "//fuzz/proto:mach_message_cc_proto", + "//fuzz/proto:mig_generated_cc_proto", + "//fuzz/xnu/osfmk:api_headers", + "//third_party/bootstrap_cmds:mig_headers", + ], +) + +cc_library( + name = "mig_types", + srcs = [ + "mig_types.cc", + "//third_party/bootstrap_cmds:mig_generated.cc", + ], + hdrs = [ + "mig_generated.h", + "mig_types.h", + ], + visibility = [ + ], + deps = [ + ":mach_message", + "//fuzz/proto:mig_generated_cc_proto", + "//fuzz/proto:mig_types_cc_proto", + "//fuzz/xnu/osfmk:api_headers", + "//third_party/bootstrap_cmds:mig_headers", + ], +) + +cc_library( + name = "command", + srcs = [ + "command.cc", + ], + hdrs = [ + "command.h", + ], + deps = [ + ":bsd", + ":mach_message", + ":mig_types", + "//fuzz/common:utility", + "//fuzz/proto:mach_traps_cc_proto", + "//fuzz/proto:session_cc_proto", + "//tools/generate_syscall:call_bsd_syscalls_generated", + "@com_google_absl//absl/strings:str_format", + ], +) + +cc_test( + name = "command_test", + srcs = [ + "command_test.cc", + ], + deps = [ + ":command", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "command_thread", + srcs = [ + "command_thread.cc", + ], + hdrs = [ + "command_thread.h", + ], + deps = [ + ":command", + "//fuzz/xnu/osfmk:api_headers", + ], +) + +cc_library( + name = "session", + srcs = ["session.cc"], + hdrs = ["session.h"], + visibility = [ + "//fuzz/target:__pkg__", + ], + deps = [ + ":command_thread", + "//fuzz/proto:session_cc_proto", + "//fuzz/host", + # "//fuzz/host/hypercall", + # "//fuzz/xnu/osfmk:api_headers", + ], +) diff --git a/fuzz/handlers/bsd.cc b/fuzz/handlers/bsd.cc new file mode 100644 index 0000000..b1c86a9 --- /dev/null +++ b/fuzz/handlers/bsd.cc @@ -0,0 +1,543 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include + +#include + +#include "absl/strings/str_join.h" +#include "bsd_types.h" +#include "fuzz/proto/bsd.pb.h" +#include "fuzz/proto/bsd_enums.pb.h" +#include "fuzz/proto/bsd_types.pb.h" +#include "fuzz/host/host.h" +#include "fuzz/xnu/bsd/api/backend.h" + +#ifndef __APPLE__ +__uint32_t ntohl(__uint32_t _data) { + return __builtin_bswap32(_data); +} + +__uint16_t ntohs(__uint16_t _data) { + return __builtin_bswap16(_data); +} +#endif // __APPLE__ + +// TODO(nedwill): support multiple addresses of each type below, +// not just one of each type +void get_in6_addr(struct in6_addr *sai, enum In6Addr addr) { + memset(sai, 0, sizeof(*sai)); + switch (addr) { + case IN6_ADDR_SELF: { + sai->__u6_addr.__u6_addr32[0] = 16810238; + sai->__u6_addr.__u6_addr32[0] = 0; + sai->__u6_addr.__u6_addr32[0] = 0; + sai->__u6_addr.__u6_addr32[0] = 16777216; + // assert(IN6_IS_ADDR_SELF(sai)); + break; + } + case IN6_ADDR_LINK_LOCAL: { + sai->s6_addr[0] = 0xfe; + sai->s6_addr[1] = 0x80; + // TODO(nedwill): set other fields? + assert(IN6_IS_ADDR_LINKLOCAL(sai)); + break; + } + case IN6_ADDR_LOOPBACK: { + *sai = {{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + static_cast(addr)}}}; + assert(IN6_IS_ADDR_LOOPBACK(sai)); + break; + } + case IN6_ADDR_REAL: + case MAYBE_LOCALHOST: { + *sai = {{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + static_cast(addr)}}}; + break; + } + case IN6_ADDR_V4COMPAT: { + sai->s6_addr[12] = 1; + assert(IN6_IS_ADDR_V4COMPAT(sai)); + break; + } + case IN6_ADDR_V4MAPPED: { + *reinterpret_cast(&sai->s6_addr[8]) = 0xffff0000; + assert(IN6_IS_ADDR_V4MAPPED(sai)); + break; + } + case IN6_ADDR_6TO4: { + // sai->s6_addr16[0] = ntohs(0x2002); + // assert(IN6_IS_ADDR_6TO4(sai)); + break; + } + case IN6_ADDR_LINKLOCAL: { + sai->s6_addr[0] = 0xfe; + sai->s6_addr[1] = 0x80; + assert(IN6_IS_ADDR_LINKLOCAL(sai)); + break; + } + case IN6_ADDR_SITELOCAL: { + sai->s6_addr[0] = 0xfe; + sai->s6_addr[1] = 0xc0; + assert(IN6_IS_ADDR_SITELOCAL(sai)); + break; + } + case IN6_ADDR_MULTICAST: { + sai->s6_addr[0] = 0xff; + assert(IN6_IS_ADDR_MULTICAST(sai)); + break; + } + case IN6_ADDR_UNIQUE_LOCAL: { + sai->s6_addr[0] = 0xfc; + assert(IN6_IS_ADDR_UNIQUE_LOCAL(sai)); + break; + } + case IN6_ADDR_MC_NODELOCAL: { + sai->s6_addr[0] = 0xff; + sai->s6_addr[1] = __IPV6_ADDR_SCOPE_NODELOCAL; + assert(IN6_IS_ADDR_MC_NODELOCAL(sai)); + break; + } + case IN6_ADDR_MC_INTFACELOCAL: { + sai->s6_addr[0] = 0xff; + sai->s6_addr[1] = __IPV6_ADDR_SCOPE_INTFACELOCAL; + assert(IN6_IS_ADDR_MC_INTFACELOCAL(sai)); + break; + } + case IN6_ADDR_MC_LINKLOCAL: { + sai->s6_addr[0] = 0xff; + sai->s6_addr[1] = __IPV6_ADDR_SCOPE_LINKLOCAL; + assert(IN6_IS_ADDR_MC_LINKLOCAL(sai)); + break; + } + case IN6_ADDR_MC_SITELOCAL: { + sai->s6_addr[0] = 0xff; + sai->s6_addr[1] = __IPV6_ADDR_SCOPE_SITELOCAL; + assert(IN6_IS_ADDR_MC_SITELOCAL(sai)); + break; + } + case IN6_ADDR_MC_ORGLOCAL: { + sai->s6_addr[0] = 0xff; + sai->s6_addr[1] = __IPV6_ADDR_SCOPE_ORGLOCAL; + assert(IN6_IS_ADDR_MC_ORGLOCAL(sai)); + break; + } + case IN6_ADDR_MC_GLOBAL: { + sai->s6_addr[0] = 0xff; + sai->s6_addr[1] = __IPV6_ADDR_SCOPE_GLOBAL; + assert(IN6_IS_ADDR_MC_GLOBAL(sai)); + break; + } + case IN6_ADDR_UNSPECIFIED: + case IN6_ADDR_ANY: { + assert(IN6_IS_ADDR_UNSPECIFIED(sai)); + break; + } + case IN6_ADDR_LOCAL_ADDRESS: { + // Discovered this address dynamically + // fe80:0001:0000:0000:a8aa:aaaa:aaaa:aaaa + sai->s6_addr16[0] = 0xfe80; + sai->s6_addr16[1] = 0x0001; + sai->s6_addr16[4] = 0xa8aa; + sai->s6_addr16[5] = 0xaaaa; + sai->s6_addr16[6] = 0xaaaa; + sai->s6_addr16[7] = 0xaaaa; + } + // *sai = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint8_t)addr}; + } +} + +void get_sockaddr6(struct sockaddr_in6 *sai, const SockAddr6 &sa6) { + sai->sin6_len = sizeof(struct sockaddr_in6); + sai->sin6_family = static_cast(AF_INET6); // sa6.family(); + sai->sin6_port = static_cast(sa6.port()); + sai->sin6_flowinfo = sa6.flow_info(); + get_in6_addr(&sai->sin6_addr, sa6.sin6_addr()); + sai->sin6_scope_id = sa6.sin6_scope_id(); +} + +std::string get_sockaddr(const SockAddr &sockaddr) { + std::string dat; + switch (sockaddr.sockaddr_case()) { + case SockAddr::kSockaddrGeneric: { + const SockAddrGeneric &sag = sockaddr.sockaddr_generic(); + // data size + sizeof(sa_len) + sizeof(sa_family) + struct sockaddr_generic sag_s = { + .sa_len = static_cast(sizeof(sockaddr_generic) + + sag.sa_data().size()), + .sa_family = static_cast(sag.sa_family()), + }; + + dat = std::string(reinterpret_cast(&sag_s), + reinterpret_cast(&sag_s) + sizeof(sag_s)); + dat += sag.sa_data(); + break; + } + case SockAddr::kSockaddr4: { + struct sockaddr_in sai = { + .sin_len = sizeof(struct sockaddr_in), + .sin_family = + AF_INET, // (unsigned char)sockaddr.sockaddr4().sin_family(), + .sin_port = + static_cast(sockaddr.sockaddr4().sin_port()), + .sin_addr = {static_cast( + sockaddr.sockaddr4().sin_addr())}, + .sin_zero = {}, + }; + dat = std::string(reinterpret_cast(&sai), + reinterpret_cast(&sai) + sizeof(sai)); + break; + } + case SockAddr::kSockaddr6: { + struct sockaddr_in6 sai = {}; + get_sockaddr6(&sai, sockaddr.sockaddr6()); + dat = std::string((char *)&sai, (char *)&sai + sizeof(sai)); + break; + } + // case SockAddr::kRawBytes: { + // dat = sockaddr.raw_bytes(); + // break; + // } + case SockAddr::SOCKADDR_NOT_SET: { + break; + } + } + if (is_verbose) { + g_host->Log("get_sockaddr: returning dat\n"); + // TODO(nedwill): have this whole function return std::string + std::vector dat_vec; + for (const uint8_t ch : dat) { + dat_vec.push_back(ch); + } + std::string result; + g_host->Log(absl::StrCat("{", absl::StrJoin(dat_vec, ", "), "}\n")); + } + return dat; +} + +std::string get_ip6_hdr(const Ip6Hdr &hdr, uint16_t expected_size) { + struct ip6_hdr ip6_hdr; + memset(&ip6_hdr, 0, sizeof(ip6_hdr)); + get_in6_addr(&ip6_hdr.ip6_src, hdr.ip6_src()); + get_in6_addr(&ip6_hdr.ip6_dst, hdr.ip6_src()); + ip6_hdr.ip6_ctlun.ip6_un2_vfc = IPV6_VERSION; + // How TF does flow work? + // ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_flow = hdr.ip6_hdrctl().ip6_un1_flow(); + ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_plen = + __builtin_bswap16(expected_size); // hdr.ip6_hdrctl().ip6_un1_plen(); + ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_nxt = hdr.ip6_hdrctl().ip6_un1_nxt(); + ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_hlim = hdr.ip6_hdrctl().ip6_un1_hlim(); + std::string dat(reinterpret_cast(&ip6_hdr), + reinterpret_cast(&ip6_hdr) + sizeof(ip6_hdr)); + return dat; +} + +std::string get_ip_hdr(const IpHdr &hdr, size_t expected_size) { + struct in_addr ip_src = {.s_addr = static_cast(hdr.ip_src())}; + struct in_addr ip_dst = {.s_addr = static_cast(hdr.ip_dst())}; + struct ip ip_hdr = { + .ip_hl = 5, // TODO(nedwill): support options // hdr.ip_hl(), + .ip_v = IPV4, // hdr.ip_v(), + .ip_tos = static_cast(hdr.ip_tos()), + .ip_len = static_cast(__builtin_bswap16(expected_size)), + .ip_id = static_cast(hdr.ip_id()), + .ip_off = static_cast(hdr.ip_off()), + .ip_ttl = static_cast(hdr.ip_ttl()), + .ip_p = static_cast(hdr.ip_p()), + .ip_sum = 0, + .ip_src = ip_src, + .ip_dst = ip_dst, + }; + std::string dat(reinterpret_cast(&ip_hdr), + reinterpret_cast(&ip_hdr) + sizeof(ip_hdr)); + return dat; +} + +// message TcpHdr { +// required Port th_sport = 1; +// required Port th_dport = 2; +// required uint32 th_seq = 3; +// required uint32 th_ack = 4; +// required uint32 th_off = 5; +// repeated TcpFlag th_flags = 6; +// required uint32 th_win = 7; +// required uint32 th_sum = 8; +// required uint32 th_urp = 9; +// } + +std::string get_tcp_hdr(const TcpHdr &hdr) { + struct tcphdr tcphdr = { + .th_sport = static_cast(hdr.th_sport()), + .th_dport = static_cast(hdr.th_dport()), + .th_seq = __builtin_bswap32(hdr.th_seq()), + .th_ack = __builtin_bswap32(hdr.th_ack()), + .th_off = hdr.th_off(), + .th_flags = 0, + .th_win = static_cast(hdr.th_win()), + .th_sum = 0, + .th_urp = static_cast(hdr.th_urp()), + }; + + for (const int flag : hdr.th_flags()) { + tcphdr.th_flags ^= flag; + } + + // Prefer pure syn + if (hdr.is_pure_syn()) { + tcphdr.th_flags &= ~(TH_RST | TH_ACK); + tcphdr.th_flags |= TH_SYN; + } else if (hdr.is_pure_ack()) { + tcphdr.th_flags &= ~(TH_RST | TH_SYN); + tcphdr.th_flags |= TH_ACK; + } + + std::string dat(reinterpret_cast(&tcphdr), + reinterpret_cast(&tcphdr) + sizeof(tcphdr)); + return dat; +} + +std::string get_icmp6_hdr(const Icmp6Hdr &hdr) { + struct icmp6_hdr icmp6_hdr = { + .icmp6_type = static_cast(hdr.icmp6_type()), + .icmp6_code = static_cast(hdr.icmp6_code()), + .icmp6_cksum = 0, + }; + icmp6_hdr.icmp6_dataun.icmp6_un_data32[0] = hdr.icmp6_dataun(); + + std::string dat(reinterpret_cast(&icmp6_hdr), + reinterpret_cast(&icmp6_hdr) + sizeof(icmp6_hdr)); + return dat; +} + +std::string get_ip6_route_hdr(const Ip6RtHdr &hdr) { + struct ip6_rthdr ip6_rthdr = { + .ip6r_nxt = static_cast(hdr.ip6r_nxt()), + .ip6r_len = static_cast(hdr.ip6r_len()), + .ip6r_type = static_cast(hdr.ip6r_type()), + .ip6r_segleft = static_cast(hdr.ip6r_segleft()), + }; + + std::string dat(reinterpret_cast(&ip6_rthdr), + reinterpret_cast(&ip6_rthdr) + sizeof(ip6_rthdr)); + return dat; +} + +std::string get_ip6_route0_hdr(const Ip6Rt0Hdr &hdr) { + struct ip6_rthdr0 ip6_rthdr0 = {}; + ip6_rthdr0.ip6r0_nxt = hdr.ip6r0_nxt(); + ip6_rthdr0.ip6r0_len = hdr.ip6r0_len(); + ip6_rthdr0.ip6r0_type = hdr.ip6r0_type(); + ip6_rthdr0.ip6r0_segleft = hdr.ip6r0_segleft(); + ip6_rthdr0.ip6r0_reserved = hdr.ip6r0_reserved(); + *reinterpret_cast(&ip6_rthdr0.ip6r0_slmap[0]) = hdr.ip6r0_slmap(); + + int i = 0; + for (int in6addr : hdr.ip6r0_addr()) { + if (i >= 23) { + break; + } + + get_in6_addr(&ip6_rthdr0.ip6r0_addr[i], static_cast(in6addr)); + + i++; + } + + std::string dat(reinterpret_cast(&ip6_rthdr0), + reinterpret_cast(&ip6_rthdr0) + sizeof(ip6_rthdr0)); + return dat; +} + +std::string get_ip6_frag_hdr(const Ip6FragHdr &hdr) { + struct ip6_frag ip6_frag = { + .ip6f_nxt = static_cast(hdr.ip6f_nxt()), + .ip6f_reserved = static_cast(hdr.ip6f_reserved()), + .ip6f_offlg = static_cast(hdr.ip6f_offlg()), + .ip6f_ident = hdr.ip6f_ident(), + }; + + std::string dat(reinterpret_cast(&ip6_frag), + reinterpret_cast(&ip6_frag) + sizeof(ip6_frag)); + return dat; +} + +std::string get_ip6_ext(const Ip6Ext &hdr) { + struct ip6_ext ip6_ext = { + .ip6e_nxt = static_cast(hdr.ip6e_nxt()), + .ip6e_len = static_cast(hdr.ip6e_len()), + }; + + std::string dat(reinterpret_cast(&ip6_ext), + reinterpret_cast(&ip6_ext) + sizeof(ip6_ext)); + return dat; +} + +void get_in6_addrlifetime_64(struct in6_addrlifetime_64 *sai, + const In6AddrLifetime_64 &msg) { + sai->ia6t_expire = msg.ia6t_expire(); + sai->ia6t_preferred = msg.ia6t_preferred(); + sai->ia6t_vltime = msg.ia6t_vltime(); + sai->ia6t_pltime = msg.ia6t_pltime(); +} + +void get_ifr_name(void *dest, const IfrName name) { + switch (name) { + case LO0: { + memcpy(dest, "lo0", sizeof("lo0")); + break; + } + case STF0: { + memcpy(dest, "stf0", sizeof("stf0")); + break; + } + case IPSEC0: { + memcpy(dest, "ipsec0", sizeof("ipsec0")); + break; + } + case UTUN0: { + memcpy(dest, "utun0", sizeof("utun0")); + break; + } + } +} + +void DoTcpInput(const TcpPacket &tcp_packet) { + std::string packet_s; + + size_t expected_size = + sizeof(struct ip) + sizeof(struct tcphdr) + tcp_packet.data().size(); + packet_s += get_ip_hdr(tcp_packet.ip_hdr(), expected_size); + packet_s += get_tcp_hdr(tcp_packet.tcp_hdr()); + packet_s += tcp_packet.data(); + assert(expected_size == packet_s.size()); + + if (packet_s.empty()) { + return; + } + + // TODO(nedwill): fuzz structure of mbuf itself + void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); + if (!mbuf_data) { + return; + } + + ip_input_wrapper(mbuf_data); +} + +void DoTcp6Input(const Tcp6Packet &tcp6_packet) { + std::string packet_s; + + // TODO(nedwill): support hop-by-hop and other options + size_t expected_size = sizeof(struct tcphdr) + tcp6_packet.data().size(); + packet_s += get_ip6_hdr(tcp6_packet.ip6_hdr(), expected_size); + packet_s += get_tcp_hdr(tcp6_packet.tcp_hdr()); + packet_s += tcp6_packet.data(); + + if (packet_s.empty()) { + return; + } + + void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); + if (!mbuf_data) { + return; + } + + ip6_input_wrapper(mbuf_data); +} + +void DoIp4Packet(const Ip4Packet &packet) { + size_t expected_size = sizeof(struct ip) + packet.data().size(); + std::string packet_s = get_ip_hdr(packet.ip_hdr(), expected_size); + packet_s += packet.data(); + + void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); + if (!mbuf_data) { + return; + } + + ip_input_wrapper(mbuf_data); +} + +void DoIp6Packet(const Ip6Packet &packet) { + size_t expected_size = packet.data().size(); + std::string packet_s = get_ip6_hdr(packet.ip6_hdr(), expected_size); + packet_s += packet.data(); + + void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); + if (!mbuf_data) { + return; + } + + ip6_input_wrapper(mbuf_data); +} + +void DoIpInput(const Packet &packet) { + switch (packet.packet_case()) { + case Packet::kTcpPacket: { + DoTcpInput(packet.tcp_packet()); + break; + } + case Packet::kTcp6Packet: { + DoTcp6Input(packet.tcp6_packet()); + break; + } + case Packet::kIp4Packet: { + DoIp4Packet(packet.ip4_packet()); + break; + } + case Packet::kIp6Packet: { + DoIp6Packet(packet.ip6_packet()); + break; + } + case Packet::kRawIp4: { + void *mbuf_data = get_mbuf_data(packet.raw_ip4().data(), + packet.raw_ip4().size(), PKTF_LOOP); + if (!mbuf_data) { + return; + } + + ip_input_wrapper(mbuf_data); + break; + } + case Packet::kRawIp6: { + void *mbuf_data = get_mbuf_data(packet.raw_ip6().data(), + packet.raw_ip6().size(), PKTF_LOOP); + if (!mbuf_data) { + return; + } + + ip6_input_wrapper(mbuf_data); + break; + } + case Packet::PACKET_NOT_SET: { + break; + } + } +} diff --git a/fuzz/types.h b/fuzz/handlers/bsd_types.h similarity index 54% rename from fuzz/types.h rename to fuzz/handlers/bsd_types.h index 473f3c1..c4226fa 100644 --- a/fuzz/types.h +++ b/fuzz/handlers/bsd_types.h @@ -29,12 +29,21 @@ #ifndef FUZZ_TYPES_H_ #define FUZZ_TYPES_H_ +#include + +#include + +#include "fuzz/proto/bsd.pb.h" +#include "fuzz/proto/bsd_types.pb.h" + #define IPV6_VERSION 0x60 #define PKTF_LOOP 0x2000 +typedef char *caddr_t; + struct necp_tlv_header { - u_int8_t type; - u_int32_t length; + uint8_t type; + uint32_t length; } __attribute__((__packed__)); struct sockaddr_generic { @@ -87,12 +96,6 @@ struct sockaddr_in6 { #define s6_addr16 __u6_addr.__u6_addr16 #define s6_addr32 __u6_addr.__u6_addr32 -#ifndef __APPLE__ -__uint32_t ntohl(__uint32_t _data) { return __builtin_bswap32(_data); } - -__uint16_t ntohs(__uint16_t _data) { return __builtin_bswap16(_data); } -#endif // __APPLE__ - /* * Unspecified */ @@ -192,10 +195,10 @@ __uint16_t ntohs(__uint16_t _data) { return __builtin_bswap16(_data); } (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_INTFACELOCAL)) struct in6_addrlifetime_64 { - u_int64_t ia6t_expire; - u_int64_t ia6t_preferred; - u_int32_t ia6t_vltime; - u_int32_t ia6t_pltime; + uint64_t ia6t_expire; + uint64_t ia6t_preferred; + uint32_t ia6t_vltime; + uint32_t ia6t_pltime; }; #define IFNAMSIZ 16 @@ -231,12 +234,12 @@ struct ifreq { struct ip6_hdr { union { struct ip6_hdrctl { - u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ - u_int16_t ip6_un1_plen; /* payload length */ - u_int8_t ip6_un1_nxt; /* next header */ - u_int8_t ip6_un1_hlim; /* hop limit */ + uint32_t ip6_un1_flow; /* 20 bits of flow-ID */ + uint16_t ip6_un1_plen; /* payload length */ + uint8_t ip6_un1_nxt; /* next header */ + uint8_t ip6_un1_hlim; /* hop limit */ } ip6_un1; - u_int8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ + uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ @@ -245,12 +248,12 @@ struct ip6_hdr { struct ip { u_int ip_hl : 4, /* header length */ ip_v : 4; /* version */ - u_char ip_tos; /* type of service */ + uint8_t ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ - u_char ip_ttl; /* time to live */ - u_char ip_p; /* protocol */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; @@ -272,44 +275,183 @@ struct tcphdr { }; struct icmp6_hdr { - u_int8_t icmp6_type; /* type field */ - u_int8_t icmp6_code; /* code field */ - u_int16_t icmp6_cksum; /* checksum field */ + uint8_t icmp6_type; /* type field */ + uint8_t icmp6_code; /* code field */ + uint16_t icmp6_cksum; /* checksum field */ union { - u_int32_t icmp6_un_data32[1]; /* type-specific field */ - u_int16_t icmp6_un_data16[2]; /* type-specific field */ - u_int8_t icmp6_un_data8[4]; /* type-specific field */ + uint32_t icmp6_un_data32[1]; /* type-specific field */ + uint16_t icmp6_un_data16[2]; /* type-specific field */ + uint8_t icmp6_un_data8[4]; /* type-specific field */ } icmp6_dataun; } __attribute__((__packed__)); struct ip6_rthdr { - u_int8_t ip6r_nxt; /* next header */ - u_int8_t ip6r_len; /* length in units of 8 octets */ - u_int8_t ip6r_type; /* routing type */ - u_int8_t ip6r_segleft; /* segments left */ - /* followed by routing type specific data */ + uint8_t ip6r_nxt; /* next header */ + uint8_t ip6r_len; /* length in units of 8 octets */ + uint8_t ip6r_type; /* routing type */ + uint8_t ip6r_segleft; /* segments left */ + /* followed by routing type specific data */ } __attribute__((__packed__)); struct ip6_rthdr0 { - u_int8_t ip6r0_nxt; /* next header */ - u_int8_t ip6r0_len; /* length in units of 8 octets */ - u_int8_t ip6r0_type; /* always zero */ - u_int8_t ip6r0_segleft; /* segments left */ - u_int8_t ip6r0_reserved; /* reserved field */ - u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ + uint8_t ip6r0_nxt; /* next header */ + uint8_t ip6r0_len; /* length in units of 8 octets */ + uint8_t ip6r0_type; /* always zero */ + uint8_t ip6r0_segleft; /* segments left */ + uint8_t ip6r0_reserved; /* reserved field */ + uint8_t ip6r0_slmap[3]; /* strict/loose bit map */ struct in6_addr ip6r0_addr[23]; /* up to 23 addresses */ } __attribute__((__packed__)); struct ip6_frag { - u_int8_t ip6f_nxt; /* next header */ - u_int8_t ip6f_reserved; /* reserved field */ - u_int16_t ip6f_offlg; /* offset, reserved, and flag */ - u_int32_t ip6f_ident; /* identification */ + uint8_t ip6f_nxt; /* next header */ + uint8_t ip6f_reserved; /* reserved field */ + uint16_t ip6f_offlg; /* offset, reserved, and flag */ + uint32_t ip6f_ident; /* identification */ } __attribute__((__packed__)); struct ip6_ext { - u_int8_t ip6e_nxt; - u_int8_t ip6e_len; + uint8_t ip6e_nxt; + uint8_t ip6e_len; } __attribute__((__packed__)); +typedef uint64_t user_addr_t; + +struct kevent { + uintptr_t ident; /* identifier for this event */ + int16_t filter; /* filter for event */ + uint16_t flags; /* general flags */ + uint32_t fflags; /* filter-specific flags */ + intptr_t data; /* filter-specific data */ + void *udata; /* opaque user data identifier */ + + kevent(uintptr_t ident, int16_t filter, uint16_t flags, uint32_t fflags, + intptr_t data, void *udata) + : ident(ident), + filter(filter), + flags(flags), + fflags(fflags), + data(data), + udata(udata) {} +}; + +struct msghdr { + void *msg_name; /* [XSI] optional address */ + __socklen_t msg_namelen; /* [XSI] size of address */ + struct iovec *msg_iov; /* [XSI] scatter/gather array */ + int msg_iovlen; /* [XSI] # elements in msg_iov */ + void *msg_control; /* [XSI] ancillary data, see below */ + __socklen_t msg_controllen; /* [XSI] ancillary data buffer len */ + int msg_flags; /* [XSI] flags on received message */ +}; + +typedef __uint64_t rlim_t; +struct rlimit { + rlim_t rlim_cur; /* current (soft) limit */ + rlim_t rlim_max; /* maximum value for rlim_cur */ +}; + +struct ipv6_prefix { + struct in6_addr ipv6_prefix; + uint32_t prefix_len; +}; + +typedef u_int32_t necp_kernel_policy_result; +typedef u_int32_t necp_kernel_policy_filter; +typedef unsigned char uuid_t[16]; + +typedef union { + u_int tunnel_interface_index; + u_int scoped_interface_index; + u_int32_t flow_divert_control_unit; + u_int32_t filter_control_unit; + u_int32_t pass_flags; + u_int32_t drop_flags; +} necp_kernel_policy_routing_result_parameter; + +#define NECP_MAX_NETAGENTS 16 +#define NAT64_MAX_NUM_PREFIXES 4 + +struct necp_aggregate_result { + necp_kernel_policy_result routing_result; + necp_kernel_policy_routing_result_parameter routing_result_parameter; + necp_kernel_policy_filter filter_control_unit; + u_int32_t flow_divert_aggregate_unit; + necp_kernel_policy_result service_action; + uuid_t service_uuid; + u_int32_t service_flags; + u_int32_t service_data; + u_int routed_interface_index; + u_int32_t policy_id; + uuid_t netagents[NECP_MAX_NETAGENTS]; + u_int32_t netagent_use_flags[NECP_MAX_NETAGENTS]; + struct ipv6_prefix nat64_prefixes[NAT64_MAX_NUM_PREFIXES]; + u_int8_t mss_recommended; +}; + +/* union for signal handlers */ +union __sigaction_u { + void (*__sa_handler)(int); + void (*__sa_sigaction)(int, struct __siginfo *, void *); +}; + +/* + * Signal vector "template" used in sigaction call. + */ +struct sigaction { + union __sigaction_u __sigaction_u; /* signal handler */ + sigset_t sa_mask; /* signal mask to apply */ + int sa_flags; /* see signal options below */ +}; + +struct rusage { + struct timeval ru_utime; /* user time used (PL) */ + struct timeval ru_stime; /* system time used (PL) */ +#if __DARWIN_C_LEVEL < __DARWIN_C_FULL + long ru_opaque[14]; /* implementation defined */ +#else + /* + * Informational aliases for source compatibility with programs + * that need more information than that provided by standards, + * and which do not mind being OS-dependent. + */ + long ru_maxrss; /* max resident set size (PL) */ +#define ru_first ru_ixrss /* internal: ruadd() range start */ + long ru_ixrss; /* integral shared memory size (NU) */ + long ru_idrss; /* integral unshared data (NU) */ + long ru_isrss; /* integral unshared stack (NU) */ + long ru_minflt; /* page reclaims (NU) */ + long ru_majflt; /* page faults (NU) */ + long ru_nswap; /* swaps (NU) */ + long ru_inblock; /* block input operations (atomic) */ + long ru_oublock; /* block output operations (atomic) */ + long ru_msgsnd; /* messages sent (atomic) */ + long ru_msgrcv; /* messages received (atomic) */ + long ru_nsignals; /* signals received (atomic) */ + long ru_nvcsw; /* voluntary context switches (atomic) */ + long ru_nivcsw; /* involuntary " */ +#define ru_last ru_nivcsw /* internal: ruadd() range end */ +#endif /* __DARWIN_C_LEVEL >= __DARWIN_C_FULL */ +}; + +enum { + NFSSVC_NFSD = 0x004, + NFSSVC_ADDSOCK = 0x008, + NFSSVC_EXPORTSTATS = 0x010, + NFSSVC_USERSTATS = 0x020, + NFSSVC_USERCOUNT = 0x040, + NFSSVC_ZEROSTATS = 0x080, + NFSSVC_SRVSTATS = 0x100, + NFSSVC_EXPORT = 0x200 +}; + +void get_sockaddr6(struct sockaddr_in6 *sai, const SockAddr6 &sa6); +std::string get_sockaddr(const SockAddr &sockaddr); +void get_in6_addrlifetime_64(struct in6_addrlifetime_64 *sai, + const In6AddrLifetime_64 &msg); +void get_ifr_name(void *dest, const IfrName name); + +void DoIpInput(const Packet &packet); +void DoNecpClientAction(const NecpClientAction &necp_client_action); + #endif /* FUZZ_TYPES_H_ */ diff --git a/fuzz/handlers/command.cc b/fuzz/handlers/command.cc new file mode 100644 index 0000000..dc39b3c --- /dev/null +++ b/fuzz/handlers/command.cc @@ -0,0 +1,783 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/handlers/command.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "fuzz/common/utility.h" +#include "fuzz/handlers/bsd_types.h" +#include "fuzz/handlers/mach_message.h" +#include "fuzz/handlers/mig_types.h" +#include "fuzz/host/host.h" +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/proto/bsd.pb.h" +#include "fuzz/proto/bsd_types.pb.h" +#include "fuzz/proto/common.pb.h" +#include "fuzz/proto/mach_traps.pb.h" +#include "fuzz/proto/session.pb.h" +#include "fuzz/xnu/bsd/api/backend.h" +#include "fuzz/xnu/bsd/api/ioctl.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "fuzz/xnu/osfmk/api/types.h" +#include "third_party/bootstrap_cmds/include/mach/message.h" +#include "tools/generate_syscall/call_bsd_syscall.h" +#include "tools/generate_syscall/syscall_wrappers_generated.h" + +struct __jmp_buf_tag; + +vm_prot_t GetProtection(const ::google::protobuf::RepeatedField &prot) { + vm_prot_t result = 0; + for (const auto &flag : prot) { + result |= flag; + } + return result; +} + +// "bsd/sys/kern_control.h" +struct sockaddr_ctl { + u_char sc_len; /* depends on size of bundle ID string */ + u_char sc_family; /* AF_SYSTEM */ + u_int16_t ss_sysaddr; /* AF_SYS_KERNCONTROL */ + u_int32_t sc_id; /* Controller unique identifier */ + u_int32_t sc_unit; /* Developer private unit number */ + u_int32_t sc_reserved[5]; +}; + +struct user_nfsd_args { + int sock; /* Socket to serve */ + user_addr_t name + __attribute((aligned(8))); /* Client addr for connection based sockets */ + int namelen; /* Length of name */ +}; + +// TODO(nedwill): move this function to XNU side since the values don't match +// Linux. To update this list use third_party/xnu/bsd/sys/errno.h +std::string ErrorToString(int error) { + switch (error) { + case 0: { + return "SUCCESS"; + } + case 102: { + return "EOPNOTSUPP"; + } + case 52: { + return "ENETRESET"; + } + case 22: { + return "EINVAL"; + } + case 13: { + return "EACCES"; + } + case 41: { + return "EPROTOTYPE"; + } + case 9: { + return "EBADF"; + } + case 17: { + return "EEXIST"; + } + default: { + g_host->Log(absl::StrFormat("Unknown error code %d\n", error)); + GetHypercallInterface()->Abort(); + } + } +} + +namespace { + +absl::flat_hash_map mach_message_return_strings = { + {MachMsgReturnT::MACH_MSG_SUCCESS, "MACH_MSG_SUCCESS"}, + {MachMsgReturnT::MACH_MSG_MASK, "MACH_MSG_MASK"}, + {MachMsgReturnT::MACH_MSG_IPC_SPACE, "MACH_MSG_IPC_SPACE"}, + {MachMsgReturnT::MACH_MSG_VM_SPACE, "MACH_MSG_VM_SPACE"}, + {MachMsgReturnT::MACH_MSG_IPC_KERNEL, "MACH_MSG_IPC_KERNEL"}, + {MachMsgReturnT::MACH_MSG_VM_KERNEL, "MACH_MSG_VM_KERNEL"}, + {MachMsgReturnT::MACH_SEND_IN_PROGRESS, "MACH_SEND_IN_PROGRESS"}, + {MachMsgReturnT::MACH_SEND_INVALID_DATA, "MACH_SEND_INVALID_DATA"}, + {MachMsgReturnT::MACH_SEND_INVALID_DEST, "MACH_SEND_INVALID_DEST"}, + {MachMsgReturnT::MACH_SEND_TIMED_OUT, "MACH_SEND_TIMED_OUT"}, + {MachMsgReturnT::MACH_SEND_INVALID_VOUCHER, "MACH_SEND_INVALID_VOUCHER"}, + {MachMsgReturnT::MACH_SEND_INTERRUPTED, "MACH_SEND_INTERRUPTED"}, + {MachMsgReturnT::MACH_SEND_MSG_TOO_SMALL, "MACH_SEND_MSG_TOO_SMALL"}, + {MachMsgReturnT::MACH_SEND_INVALID_REPLY, "MACH_SEND_INVALID_REPLY"}, + {MachMsgReturnT::MACH_SEND_INVALID_RIGHT, "MACH_SEND_INVALID_RIGHT"}, + {MachMsgReturnT::MACH_SEND_INVALID_NOTIFY, "MACH_SEND_INVALID_NOTIFY"}, + {MachMsgReturnT::MACH_SEND_INVALID_MEMORY, "MACH_SEND_INVALID_MEMORY"}, + {MachMsgReturnT::MACH_SEND_NO_BUFFER, "MACH_SEND_NO_BUFFER"}, + {MachMsgReturnT::MACH_SEND_TOO_LARGE, "MACH_SEND_TOO_LARGE"}, + {MachMsgReturnT::MACH_SEND_INVALID_TYPE, "MACH_SEND_INVALID_TYPE"}, + {MachMsgReturnT::MACH_SEND_INVALID_HEADER, "MACH_SEND_INVALID_HEADER"}, + {MachMsgReturnT::MACH_SEND_INVALID_TRAILER, "MACH_SEND_INVALID_TRAILER"}, + {MachMsgReturnT::MACH_SEND_INVALID_CONTEXT, "MACH_SEND_INVALID_CONTEXT"}, + {MachMsgReturnT::MACH_SEND_INVALID_OPTIONS, "MACH_SEND_INVALID_OPTIONS"}, + {MachMsgReturnT::MACH_SEND_INVALID_RT_OOL_SIZE, "MACH_SEND_INVALID_RT_OOL_SIZE"}, + {MachMsgReturnT::MACH_SEND_NO_GRANT_DEST, "MACH_SEND_NO_GRANT_DEST"}, + {MachMsgReturnT::MACH_SEND_MSG_FILTERED, "MACH_SEND_MSG_FILTERED"}, + {MachMsgReturnT::MACH_SEND_AUX_TOO_SMALL, "MACH_SEND_AUX_TOO_SMALL"}, + {MachMsgReturnT::MACH_SEND_AUX_TOO_LARGE, "MACH_SEND_AUX_TOO_LARGE"}, + {MachMsgReturnT::MACH_RCV_IN_PROGRESS, "MACH_RCV_IN_PROGRESS"}, + {MachMsgReturnT::MACH_RCV_INVALID_NAME, "MACH_RCV_INVALID_NAME"}, + {MachMsgReturnT::MACH_RCV_TIMED_OUT, "MACH_RCV_TIMED_OUT"}, + {MachMsgReturnT::MACH_RCV_TOO_LARGE, "MACH_RCV_TOO_LARGE"}, + {MachMsgReturnT::MACH_RCV_INTERRUPTED, "MACH_RCV_INTERRUPTED"}, + {MachMsgReturnT::MACH_RCV_PORT_CHANGED, "MACH_RCV_PORT_CHANGED"}, + {MachMsgReturnT::MACH_RCV_INVALID_NOTIFY, "MACH_RCV_INVALID_NOTIFY"}, + {MachMsgReturnT::MACH_RCV_INVALID_DATA, "MACH_RCV_INVALID_DATA"}, + {MachMsgReturnT::MACH_RCV_PORT_DIED, "MACH_RCV_PORT_DIED"}, + {MachMsgReturnT::MACH_RCV_IN_SET, "MACH_RCV_IN_SET"}, + {MachMsgReturnT::MACH_RCV_HEADER_ERROR, "MACH_RCV_HEADER_ERROR"}, + {MachMsgReturnT::MACH_RCV_BODY_ERROR, "MACH_RCV_BODY_ERROR"}, + {MachMsgReturnT::MACH_RCV_INVALID_TYPE, "MACH_RCV_INVALID_TYPE"}, + {MachMsgReturnT::MACH_RCV_SCATTER_SMALL, "MACH_RCV_SCATTER_SMALL"}, + {MachMsgReturnT::MACH_RCV_INVALID_TRAILER, "MACH_RCV_INVALID_TRAILER"}, + {MachMsgReturnT::MACH_RCV_IN_PROGRESS_TIMED, "MACH_RCV_IN_PROGRESS_TIMED"}, + {MachMsgReturnT::MACH_RCV_INVALID_REPLY, "MACH_RCV_INVALID_REPLY"}, + {MachMsgReturnT::MACH_RCV_INVALID_ARGUMENTS, "MACH_RCV_INVALID_ARGUMENTS"}, +}; + +std::string MachMessageReturnValueToString(mach_msg_return_t rv) { + auto it = mach_message_return_strings.find(static_cast(rv)); + if (it != mach_message_return_strings.end()) { + return it->second; + } + return absl::StrFormat("MachMessageReturnValueToString: Unknown Mach message return value: 0x%x", rv); +} + +} + +int CommandHandler::HandleMachMsg2(const MachMsg2Trap &trap) const { + std::vector message; + mach_vm_address_t data = 0; + if (trap.has_data()) { + message = GetMachMsg(trap.data()); + data = reinterpret_cast(message.data()); + } + + // Start out with upper-most bit set for msg2 + uint64_t options = 0x80000000ULL << 32; + for (const auto &option : trap.options()) { + options |= option; + } + for (const auto &option : trap.options_upper_bits()) { + options |= (static_cast(option) << 32); + } + + uint64_t send_size = message.size(); + uint64_t msgh_bits = + MACH_MSGH_BITS_SET(trap.data().header().msgh_bits().remote(), + trap.data().header().msgh_bits().local(), + trap.data().header().msgh_bits().voucher(), 0); + + uint64_t msgh_bits_and_send_size = (send_size << 32) | msgh_bits; + + mach_port_name_t remote_port = GetMachPortName(trap.msgh_remote_port()); + mach_port_name_t local_port = GetMachPortName(trap.msgh_local_port()); + auto msgh_remote_and_local_port = + (static_cast(remote_port) & 0xFFFFFFFF) | + (static_cast(local_port) << 32); + + mach_port_name_t voucher_port = GetMachPortName(trap.msgh_voucher_port()); + uint32_t msgh_id = trap.msgh_id(); + auto msgh_voucher_and_id = + (static_cast(voucher_port) & 0xFFFFFFFF) | + (static_cast(msgh_id) << 32); + + auto desc_count_and_rcv_name = + (static_cast(trap.desc_count()) & 0xFFFFFFFF) | + (static_cast(GetMachPortName(trap.rcv_name())) << 32); + + auto rcv_size_and_priority = + (static_cast(trap.rcv_size()) & 0xFFFFFFFF) | + (static_cast(trap.priority()) << 32); + + auto timeout = trap.timeout(); + + host_->Log("mach_msg2_trap()\n"); + mach_msg_return_t err = mach_msg2_trap_wrapper( + data, options, msgh_bits_and_send_size, msgh_remote_and_local_port, + msgh_voucher_and_id, desc_count_and_rcv_name, rcv_size_and_priority, + timeout); + host_->Log(absl::StrFormat("mach_msg2_trap return %s\n", MachMessageReturnValueToString(err))); + return err; +} + +int CommandHandler::HandleMachPortAllocate(const MachPortAllocateTrap &trap, mach_port_name_t* name) const { + host_->Log("mach_port_allocate_trap()\n"); + mach_msg_return_t err = _kernelrpc_mach_port_allocate_trap_wrapper( + GetMachPortName(trap.target()), trap.right(), reinterpret_cast(name)); + host_->Log(absl::StrFormat("mach_port_allocate_trap return: %s\n", MachMessageReturnValueToString(err))); + return err; +} + +int CommandHandler::HandleMachPortDeallocate( + const MachPortDeallocateTrap &trap) const { + host_->Log("mach_port_deallocate_trap()\n"); + mach_msg_return_t err = _kernelrpc_mach_port_deallocate_trap_wrapper( + GetMachPortName(trap.target()), GetMachPortName(trap.name())); + host_->Log(absl::StrFormat("mach_port_deallocate_trap return 0x%x\n", err)); + return err; +} + +int CommandHandler::HandleTaskSelfTrap() const { + const int rv = task_self_trap_wrapper(); + host_->Log(absl::StrFormat("task_self_trap_wrapper() -> %d\n", rv)); + return rv; +} + +void CommandHandler::HandleCommand(const Command &command) { + // host_->Log("Saving CommandThread entrypoint\n"); + __jmp_buf_tag *jmp_buf = host_->SetKernelEntryPoint(); + if (setjmp(jmp_buf)) { + // host_->Log("syscall_return to CommandThread\n"); + return; + } + + int retval = 0; + switch (command.command_case()) { + case Command::kMachVmAllocateTrap: { + _kernelrpc_mach_vm_allocate_trap_wrapper( + GetMachPortName(command.mach_vm_allocate_trap().target()), 1, + command.mach_vm_allocate_trap().size(), + command.mach_vm_allocate_trap().flags()); + break; + } + case Command::kMachVmPurgableControl: { + _kernelrpc_mach_vm_purgable_control_trap_wrapper( + GetMachPortName(command.mach_vm_purgable_control().target()), + command.mach_vm_purgable_control().address(), + command.mach_vm_purgable_control().control(), 1); + break; + } + case Command::kMachVmDeallocateTrap: { + _kernelrpc_mach_vm_deallocate_trap_wrapper( + GetMachPortName(command.mach_vm_deallocate_trap().target()), + command.mach_vm_deallocate_trap().address(), + command.mach_vm_deallocate_trap().size()); + break; + } + case Command::kTaskDyldProcessInfoNotifyGet: { + task_dyld_process_info_notify_get_trap_wrapper( + // command.task_dyld_process_info_notify_get().names_addr(), + // command.task_dyld_process_info_notify_get().names_count_addr() + 1, 1); + break; + } + case Command::kMachVmProtectTrap: { + _kernelrpc_mach_vm_protect_trap_wrapper( + GetMachPortName(command.mach_vm_protect_trap().target()), + command.mach_vm_protect_trap().address(), + command.mach_vm_protect_trap().size(), + static_cast(command.mach_vm_protect_trap().set_maximum()), + GetProtection(command.mach_vm_protect_trap().new_protection())); + break; + } + case Command::kMachVmMapTrap: { + _kernelrpc_mach_vm_map_trap_wrapper( + GetMachPortName(command.mach_vm_map_trap().target()), + // command.mach_vm_map_trap().addr(), + 1, command.mach_vm_map_trap().size(), + command.mach_vm_map_trap().mask(), command.mach_vm_map_trap().flags(), + GetProtection(command.mach_vm_map_trap().cur_protection())); + break; + } + case Command::kMachPortAllocate: { + mach_port_name_t name = 0; + HandleMachPortAllocate(command.mach_port_allocate(), &name); + break; + } + case Command::kMachPortDeallocate: { + HandleMachPortDeallocate(command.mach_port_deallocate()); + break; + } + case Command::kMachPortModRefs: { + _kernelrpc_mach_port_mod_refs_trap_wrapper( + GetMachPortName(command.mach_port_mod_refs().target()), + GetMachPortName(command.mach_port_mod_refs().name()), + command.mach_port_mod_refs().right(), + command.mach_port_mod_refs().delta()); + break; + } + case Command::kMachPortMoveMember: { + _kernelrpc_mach_port_move_member_trap_wrapper( + GetMachPortName(command.mach_port_move_member().target()), + GetMachPortName(command.mach_port_move_member().member()), + GetMachPortName(command.mach_port_move_member().after())); + break; + } + case Command::kMachPortInsertRight: { + const int ret = _kernelrpc_mach_port_insert_right_trap_wrapper( + GetMachPortName(command.mach_port_insert_right().target()), + GetMachPortName(command.mach_port_insert_right().name()), + GetMachPortName(command.mach_port_insert_right().poly()), + command.mach_port_insert_right().polypoly()); + host_->Log(absl::StrFormat( + "mach_port_insert_right_trap_wrapper() -> %d\n", ret)); + break; + } + case Command::kMachPortInsertMember: { + _kernelrpc_mach_port_insert_member_trap_wrapper( + GetMachPortName(command.mach_port_insert_member().target()), + GetMachPortName(command.mach_port_insert_member().name()), + GetMachPortName(command.mach_port_insert_member().pset())); + break; + } + case Command::kMachPortExtractMember: { + _kernelrpc_mach_port_extract_member_trap_wrapper( + GetMachPortName(command.mach_port_extract_member().target()), + GetMachPortName(command.mach_port_extract_member().name()), + GetMachPortName(command.mach_port_extract_member().pset())); + break; + } + case Command::kMachPortConstruct: { + uint64_t name = 0; + + const MachPortOptions &options_proto = + command.mach_port_construct().options(); + + uint32_t flags = 0; + for (const auto &flag : options_proto.flags()) { + flags |= flag; + } + + mach_port_options_t options = { + .flags = flags, + .mpl = + { + .mpl_qlimit = options_proto.limits().limit(), + }, + .work_interval_port = + GetMachPortName(options_proto.work_interval_port()), + }; + int rv = task_self_trap_wrapper(); + host_->Log(absl::StrFormat("task_self_trap_wrapper() -> %d\n", rv)); + rv = _kernelrpc_mach_port_construct_trap_wrapper( + GetMachPortName(command.mach_port_construct().target()), + reinterpret_cast(&options), + command.mach_port_construct().context(), + reinterpret_cast(&name)); + if (rv == 0) { + host_->Log( + absl::StrFormat("mach_port_construct: got name %lu\n", name)); + } else { + host_->Log( + absl::StrFormat("mach_port_construct: failed with %d\n", rv)); + } + break; + } + case Command::kMachPortDestruct: { + host_->Log("mach_port_destruct()\n"); + _kernelrpc_mach_port_destruct_trap_wrapper( + GetMachPortName(command.mach_port_destruct().target()), + GetMachPortName(command.mach_port_destruct().name()), + command.mach_port_destruct().srdelta(), + command.mach_port_destruct().guard()); + break; + } + case Command::kMachReplyPort: { + const int rv = mach_reply_port_wrapper(); + host_->Log(absl::StrFormat("mach_reply_port_wrapper() -> %d\n", rv)); + break; + } + case Command::kMachPortRequestNotificationTrap: { + mach_port_name_t previous = 0; + _kernelrpc_mach_port_request_notification_trap_wrapper( + /*task=*/task_self_trap_wrapper(), + // GetMachPortName(command.mach_port_request_notification_trap().target()), + GetMachPortName(command.mach_port_request_notification_trap().name()), + command.mach_port_request_notification_trap().msgid(), + command.mach_port_request_notification_trap().sync(), + GetMachPortName( + command.mach_port_request_notification_trap().notify()), + GetMachMsgTypeName( + command.mach_port_request_notification_trap().notify_poly()), + &previous); + break; + } + case Command::kThreadSelf: { + host_->Log("thread_self()\n"); + const int rv = thread_self_trap_wrapper(); + host_->Log(absl::StrFormat("thread_self_trap_wrapper() -> %d\n", rv)); + break; + } + case Command::kTaskSelf: { + HandleTaskSelfTrap(); + break; + } + case Command::kHostSelf: { + host_->Log("host_self()\n"); + const int rv = host_self_trap_wrapper(); + host_->Log(absl::StrFormat("host_self_trap_wrapper() -> %d\n", rv)); + break; + } + case Command::kMachMsg: { + break; + user_addr_t addr = 1; + std::vector message; + if (command.mach_msg().has_msg()) { + message = GetMachMsg(command.mach_msg().msg()); + addr = reinterpret_cast(message.data()); + } + host_->Log("mach_msg()\n"); + int options = 0; + for (const auto &option : command.mach_msg().options()) { + options |= option; + } + mach_msg_trap_wrapper( + addr, options, message.size(), command.mach_msg().rcv_size(), + GetMachPortName(command.mach_msg().rcv_name()), + command.mach_msg().timeout(), command.mach_msg().priority(), + command.mach_msg().rcv_msg()); + break; + } + case Command::kMachMsgOverwrite: { + user_addr_t addr = 1; + std::vector message; + if (command.mach_msg_overwrite().has_msg()) { + message = GetMachMsg(command.mach_msg_overwrite().msg()); + addr = reinterpret_cast(message.data()); + } + host_->Log("mach_msg_overwrite()\n"); + int options = 0; + for (const auto &option : command.mach_msg_overwrite().options()) { + options |= option; + } + const int err = mach_msg_overwrite_trap_wrapper( + addr, options, message.size(), + command.mach_msg_overwrite().rcv_size(), + GetMachPortName(command.mach_msg_overwrite().rcv_name()), + command.mach_msg_overwrite().timeout(), + command.mach_msg_overwrite().priority(), + command.mach_msg_overwrite().rcv_msg()); + host_->Log(absl::StrFormat("mach_msg return 0x%x\n", err)); + break; + } + case Command::kMachMsg2Trap: { + HandleMachMsg2(command.mach_msg2_trap()); + break; + } + case Command::kHostCreateMachVoucherTrap: { + std::string recipes = command.host_create_mach_voucher_trap().recipes(); + host_create_mach_voucher_trap_wrapper( + GetMachPortName(command.host_create_mach_voucher_trap().host()), + reinterpret_cast( + recipes.data()), + static_cast(recipes.size()), 1 + // command.host_create_mach_voucher_trap().voucher(), + ); + break; + } + case Command::kMachVoucherExtractAttrRecipeTrap: { + size_t recipe_size = + command.mach_voucher_extract_attr_recipe_trap().recipe().size(); + mach_voucher_extract_attr_recipe_trap_wrapper( + GetMachPortName( + command.mach_voucher_extract_attr_recipe_trap().voucher_name()), + command.mach_voucher_extract_attr_recipe_trap().key(), + (uint8_t *)command.mach_voucher_extract_attr_recipe_trap() + .recipe() + .data(), + reinterpret_cast(&recipe_size)); + break; + } + case Command::kThreadGetSpecialReplyPort: { + thread_get_special_reply_port_wrapper(); + break; + } + case Command::kMkTimerCreateTrap: { + mk_timer_create_trap_wrapper(); + break; + } + case Command::kMkTimerDestroyTrap: { + host_->Log("mk_timer_destroy_trap()\n"); + mk_timer_destroy_trap_wrapper( + GetMachPortName(command.mk_timer_destroy_trap().name())); + break; + } + case Command::kMkTimerCancelTrap: { + mk_timer_cancel_trap_wrapper( + GetMachPortName(command.mk_timer_cancel_trap().name()), 0); + break; + } + case Command::kMkTimerArmTrap: { + mk_timer_arm_trap_wrapper( + GetMachPortName(command.mk_timer_arm_trap().name()), + command.mk_timer_arm_trap().expire_time()); + break; + } + case Command::kMkTimerArmLeewayTrap: { + mk_timer_arm_leeway_trap_wrapper( + GetMachPortName(command.mk_timer_arm_leeway_trap().name()), + command.mk_timer_arm_leeway_trap().mk_timer_flags(), + command.mk_timer_arm_leeway_trap().expire_time(), + command.mk_timer_arm_leeway_trap().mk_leeway()); + break; + } + // TODO(nedwill): fuzz all random syscalls but filter better modelled ones + case Command::kArbBsdSyscall: { + XNUDoRandomSyscall(command.arb_bsd_syscall().number(), + (void *)command.arb_bsd_syscall().arguments().data(), + command.arb_bsd_syscall().arguments().size()); + break; + } + case Command::kArbMachTrap: { + DoRandomMachTrap(command.arb_mach_trap().number(), + (void *)command.arb_mach_trap().arguments().data(), + command.arb_mach_trap().arguments().size()); + break; + } + case Command::kIoctl: { + // TODO(nedwill): pick these values more efficiently + // XXX: these mutate global state + const uint32_t fd = command.ioctl().fd(); + const uint32_t com = ioctls[command.ioctl().ioctl_idx() - 1]; + GetHypercallInterface()->BlockCopyin(); + ioctl_wrapper(fd, com, /*data=*/(caddr_t)1, nullptr); + GetHypercallInterface()->UnblockCopyin(); + break; + } + case Command::kIpInput: { + DoIpInput(command.ip_input()); + break; + } + case Command::kIoctlReal: { + host_->Log("ioctl_real()\n"); + switch (command.ioctl_real().ioctl_case()) { + case IoctlReal::kSiocaifaddrIn664: { + const In6_AliasReq_64 &req = + command.ioctl_real().siocaifaddr_in6_64(); + struct in6_aliasreq_64 alias = {}; + memcpy(&alias.ifra_name[0], req.ifra_name().data(), + std::min(req.ifra_name().size(), sizeof(alias.ifra_name))); + get_sockaddr6(&alias.ifra_addr, req.ifra_addr()); + get_sockaddr6(&alias.ifra_dstaddr, req.ifra_dstaddr()); + get_sockaddr6(&alias.ifra_prefixmask, req.ifra_prefixmask()); + for (const int flag : req.ifra_flags()) { + // make mutations that dupe a flag more useful by xoring + alias.ifra_flags ^= flag; + } + get_in6_addrlifetime_64(&alias.ifra_lifetime, req.ifra_lifetime()); + ioctl_wrapper(command.ioctl_real().fd(), siocaifaddr_in6_64, + reinterpret_cast(&alias), nullptr); + break; + } + case IoctlReal::kSiocsifflags: { + struct ifreq ifreq = {}; + for (const int flag : command.ioctl_real().siocsifflags().flags()) { + ifreq.ifr_flags |= flag; + } + // TODO(nedwill): fuzz non-loopback options here + get_ifr_name(&ifreq.ifr_name[0], LO0); + ioctl_wrapper(command.ioctl_real().fd(), siocsifflags, + reinterpret_cast(&ifreq), nullptr); + break; + } + case IoctlReal::IOCTL_NOT_SET: { + break; + } + } + } + case Command::kConnectx: { + host_->Log("connectx()\n"); + const bool has_srcaddr = command.connectx().endpoints().has_sae_srcaddr(); + + std::string srcaddr_s; + if (has_srcaddr) { + srcaddr_s = get_sockaddr(command.connectx().endpoints().sae_srcaddr()); + } + + std::string dstaddr_s = + get_sockaddr(command.connectx().endpoints().sae_dstaddr()); + + auto srcaddr = reinterpret_cast(srcaddr_s.data()); + const uint32_t srcsize = srcaddr_s.size(); + if (!has_srcaddr) { + srcaddr = 0; + assert(!srcsize); + } + + auto dstaddr = reinterpret_cast(dstaddr_s.data()); + const uint32_t dstsize = dstaddr_s.size(); + + // We ignore failure here since it's ok to try to send things regardless + uint32_t connectx_flags = 0; + for (const int flag : command.connectx().flags()) { + connectx_flags |= flag; + } + uint32_t cid = 0; + + const struct user64_sa_endpoints endpoints = { + .sae_srcif = static_cast( + command.connectx().endpoints().sae_srcif()), + .sae_srcaddr = srcaddr, + .sae_srcaddrlen = srcsize, + .sae_dstaddr = dstaddr, + .sae_dstaddrlen = dstsize}; + + // TODO(nedwill): is this a return value? + size_t len = 0; + // TODO(nedwill): add IOV mocking + connectx_wrapper(command.connectx().socket(), &endpoints, + command.connectx().associd(), connectx_flags, nullptr, 0, + &len, &cid, nullptr); + cids_.push_back(cid); + break; + } + case Command::kDisconnectx: { + host_->Log("disconnectx()\n"); + uint32_t cid = 0; + if (!cids_.empty()) { + cid = cids_[command.disconnectx().cid() % cids_.size()]; + } else { + cid = command.disconnectx().cid(); + } + disconnectx_wrapper(command.disconnectx().fd(), + command.disconnectx().associd(), cid, nullptr); + break; + } + case Command::kNecpClientAction: { + DoNecpClientAction(command.necp_client_action()); + break; + } + case Command::kNecpSessionAction: { + const size_t out_buffer_size = + command.necp_session_action().out_buffer_size() % 4096; + std::string buffer = command.necp_session_action().in_buffer(); + std::vector out_buffer(out_buffer_size); + necp_session_action_wrapper( + command.necp_session_action().necp_fd(), + command.necp_session_action().action(), + reinterpret_cast(buffer.data()), + command.necp_session_action().in_buffer().size(), out_buffer.data(), + out_buffer_size, &retval); + break; + } + case Command::kMigRoutineMachMsg: { + std::vector message = GetMigRoutineMsg( + command.mig_routine_mach_msg().routine(), + command.mig_routine_mach_msg().remote_type(), + GetMachPortName(command.mig_routine_mach_msg().remote_port()), + command.mig_routine_mach_msg().local_type(), + GetMachPortName(command.mig_routine_mach_msg().local_port()), + command.mig_routine_mach_msg().voucher_type(), + GetMachPortName(command.mig_routine_mach_msg().voucher_port())); + host_->Log("mig_routine_mach_msg()\n"); + host_->Log(absl::StrFormat("Got message: %s\n", DumpVector(message))); + int options = 0; + for (const auto &option : command.mig_routine_mach_msg().options()) { + options |= option; + } + host_->Log(absl::StrFormat( + "Got options: 0x%x, size 0x%x, rcv_size 0x%x, rcv_name 0x%x, " + "timeout 0x%x, priority 0x%x, rcv_msg 0x%x\n", + options, message.size(), command.mig_routine_mach_msg().rcv_size(), + GetMachPortName(command.mig_routine_mach_msg().rcv_name()), + command.mig_routine_mach_msg().timeout(), + command.mig_routine_mach_msg().priority(), + command.mig_routine_mach_msg().rcv_msg())); + int ret = mach_msg_trap_wrapper( + reinterpret_cast(message.data()), options, + message.size(), command.mig_routine_mach_msg().rcv_size(), + GetMachPortName(command.mig_routine_mach_msg().rcv_name()), + command.mig_routine_mach_msg().timeout(), + command.mig_routine_mach_msg().priority(), + command.mig_routine_mach_msg().rcv_msg()); + host_->Log(absl::StrFormat("mig_routine_mach_msg() -> %d\n", ret)); + break; + } + case Command::kKevent: { + std::vector changes; + for (const auto &c : command.kevent().changelist()) { + int flags = 0; + for (const auto &flag : c.flags()) { + flags |= flag; + } + changes.emplace_back(c.ident(), c.filter(), flags, c.fflags(), + reinterpret_cast(c.data()), + reinterpret_cast(c.udata())); + } + + std::vector events; + for (const auto &e : command.kevent().eventlist()) { + int flags = 0; + for (const auto &flag : e.flags()) { + flags |= flag; + } + events.emplace_back(e.ident(), e.filter(), flags, e.fflags(), + reinterpret_cast(e.data()), + reinterpret_cast(e.udata())); + } + + kevent_wrapper(command.kevent().kq(), changes.data(), changes.size(), + events.data(), events.size(), nullptr, &retval); + break; + } + case Command::kProcessorException: { + switch (command.processor_exception().exception_case()) { + case ProcessorException::kVmFault: { + const VmFaultException &vm_fault = + command.processor_exception().vm_fault(); + DoVmFault(vm_fault.addr(), vm_fault.type()); + break; + } + case ProcessorException::EXCEPTION_NOT_SET: { + break; + } + } + break; + } + case Command::kSyscall: { + CallBsdSyscall(command.syscall()); + break; + } + case Command::COMMAND_NOT_SET: { + break; + } + } + // Don't yield if we didn't do any call since we could have importantly + // yielded on the last iteration + if (command.command_case() != Command::COMMAND_NOT_SET) { + host_->Yield(); + } +} + +CommandHandler *CommandHandler::GetInstance() { + static auto *instance = new CommandHandler(g_host); + return instance; +} + +CommandHandler::CommandHandler(Host *host) : host_(host) {} diff --git a/fuzz/handlers/command.h b/fuzz/handlers/command.h new file mode 100644 index 0000000..3bf4068 --- /dev/null +++ b/fuzz/handlers/command.h @@ -0,0 +1,58 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_HANDLERS_COMMAND_H_ +#define FUZZ_HANDLERS_COMMAND_H_ + +#include +#include + +#include "fuzz/host/host.h" +#include "fuzz/proto/session.pb.h" + +class CommandHandler { + public: + explicit CommandHandler(Host *host); + ~CommandHandler() = default; + + void HandleCommand(const Command &command); + + static CommandHandler *GetInstance(); + + // Per-message implementations + int HandleMachMsg2(const MachMsg2Trap &trap) const; + int HandleMachPortAllocate(const MachPortAllocateTrap &trap, mach_port_name_t* name) const; + int HandleMachPortDeallocate(const MachPortDeallocateTrap &trap) const; + int HandleTaskSelfTrap() const; + + private: + std::vector cids_; + Host *host_; +}; + +#endif // FUZZ_HANDLERS_COMMAND_H_ diff --git a/fuzz/handlers/command_test.cc b/fuzz/handlers/command_test.cc new file mode 100644 index 0000000..dcca795 --- /dev/null +++ b/fuzz/handlers/command_test.cc @@ -0,0 +1,147 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/handlers/command.h" + +#include "gtest/gtest.h" + +// TODO(nedwill): kern_return_t from kern_return.h +#define KERN_SUCCESS 0 + +class Environment : public ::testing::Environment { + public: + ~Environment() override = default; + + // Override this to define how to set up the environment. + void SetUp() override { + is_verbose = true; + InitializeHost(); + g_host->scheduler()->SetThreadChoices(nullptr); + } +}; + +testing::Environment *const env = + testing::AddGlobalTestEnvironment(new Environment); + +MachPortAllocateTrap GetMachPortAllocateTrap(PortNumberMach target_port_number, MachPortRight right) { + MachPortAllocateTrap trap; + auto* target_port_name = new MachPortName(); + auto* target_port = new MachPort(); + target_port->set_port(target_port_number); + target_port_name->set_allocated_port(target_port); + trap.set_allocated_target(target_port_name); + trap.set_right(right); + return trap; +} + +MachMsg* GetMachMsg() { + // Set up the message header + auto* header = new MachMsgHeader(); + + // Create and set msgh_bits + auto* bits = new MachMsgHeaderBits(); + bits->set_remote(MachMsgTypeName::FUZZED_MACH_MSG_TYPE_COPY_SEND); + bits->set_local(MachMsgTypeName::FUZZED_MACH_MSG_TYPE_MAKE_SEND); + header->set_allocated_msgh_bits(bits); + + // Create and set msgh_remote_port + auto* remote_port = new MachPort(); + remote_port->set_port(PortNumberMach::CASE_0); + header->set_allocated_msgh_remote_port(remote_port); + + // Create and set msgh_local_port + auto* local_port = new MachPort(); + local_port->set_port(PortNumberMach::CASE_0); + header->set_allocated_msgh_local_port(local_port); + + // Create and set msgh_voucher_port + auto* voucher_port = new MachPortName(); + auto* voucher = new MachPort(); + voucher->set_port(PortNumberMach::CASE_0); + voucher_port->set_allocated_port(voucher); + header->set_allocated_msgh_voucher_port(voucher_port); + + // Set msgh_id + header->set_msgh_id(123); + + auto* msg = new MachMsg(); + msg->set_allocated_header(header); + + // Create and set body + auto* descriptor = new MachMsgDescriptor(); + + // Choose one descriptor type to set. Here we choose MachMsgPortDescriptor as an example. + auto* port_descriptor = new MachMsgPortDescriptor(); + auto* port = new MachPort(); + port->set_port(PortNumberMach::CASE_0); + port_descriptor->set_allocated_name(port); + port_descriptor->set_disposition(MachMsgTypeName::FUZZED_MACH_MSG_TYPE_COPY_SEND); + + descriptor->set_allocated_port(port_descriptor); + msg->add_body()->CopyFrom(*descriptor); + + // Set data + msg->set_data("test"); + + return msg; +} + +MachMsg2Trap GetMachMsg2Trap() { + MachMsg2Trap trap; + trap.add_options(FUZZED_MACH64_SEND_MSG); + trap.add_options_upper_bits(FUZZED_MACH64_SEND_KOBJECT_CALL); + // TODO(nedwill): finish implementing this + return trap; +} + +// constexpr unsigned int MACH_SEND_MSG_TOO_SMALL = 0x10000008; + +TEST(CommandHandlerTest, TestMachMsg2) { + CommandHandler handler(g_host); + + mach_port_name_t task_port_name = handler.HandleTaskSelfTrap(); + + MachPortAllocateTrap allocate_trap = GetMachPortAllocateTrap(static_cast(task_port_name), FUZZED_MACH_PORT_RIGHT_RECEIVE); + mach_port_name_t port_name = 0; + EXPECT_EQ(handler.HandleMachPortAllocate(allocate_trap, &port_name), KERN_SUCCESS); + + MachMsg2Trap msg2_trap = GetMachMsg2Trap(); + + // TODO(nedwill): finish test case for successful message case + EXPECT_EQ(handler.HandleMachMsg2(msg2_trap), MACH_SEND_MSG_TOO_SMALL); + + MachPortDeallocateTrap deallocate_trap; + + auto* target_port_name = new MachPortName(); + auto* target_port = new MachPort(); + target_port->set_port(static_cast(task_port_name)); + target_port_name->set_allocated_port(target_port); + deallocate_trap.set_allocated_target(target_port_name); + + EXPECT_EQ(handler.HandleMachPortDeallocate(deallocate_trap), KERN_SUCCESS); +} diff --git a/fuzz/handlers/command_thread.cc b/fuzz/handlers/command_thread.cc new file mode 100644 index 0000000..60d9d72 --- /dev/null +++ b/fuzz/handlers/command_thread.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/handlers/command_thread.h" + +#include "fuzz/handlers/command.h" +#include "fuzz/host/host.h" +#include "fuzz/xnu/osfmk/api/types.h" + +// One syscall/mach trap calling thread +void CommandThread(void *ptr, wait_result_t wait_result) { + // g_host->Log("In CommandThread\n"); + const auto &commands = + *static_cast *>(ptr); + + // Scaffolding for transcribing testcases to C + // uint32_t id = g_host->GetId(g_host->scheduler()->GetCurrentThreadHandle()); + // if (id == 1) { + // // Call syscalls here... + // return; + // } + // if (id == 2) { + // // Call syscalls here... + // return; + // } + // return; + + auto *command_handler = CommandHandler::GetInstance(); + + for (const Command &command : commands) { + command_handler->HandleCommand(command); + } + + // g_host->Log("Finished in CommandThread\n"); + g_host->ThreadFinished(); + __builtin_unreachable(); +} diff --git a/fuzz/api/backend.h b/fuzz/handlers/command_thread.h similarity index 82% rename from fuzz/api/backend.h rename to fuzz/handlers/command_thread.h index 94a9d23..ac462f8 100644 --- a/fuzz/api/backend.h +++ b/fuzz/handlers/command_thread.h @@ -1,5 +1,5 @@ /* - * Copyright 2021 Google LLC + * Copyright 2024 Google LLC * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -26,13 +26,9 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -#ifndef FUZZ_BACKEND_H_ -#define FUZZ_BACKEND_H_ +#ifndef COMMAND_THREAD_H_ +#define COMMAND_THREAD_H_ -bool init_proc(void); -void clear_all(); -void* get_mbuf_data(const char* data, size_t size, int pktflags); -void ip_input_wrapper(void* m); -void ip6_input_wrapper(void* m); +void CommandThread(void *ptr, int wait_result); -#endif // FUZZ_BACKEND_H_ +#endif diff --git a/fuzz/handlers/mach_message.cc b/fuzz/handlers/mach_message.cc new file mode 100644 index 0000000..0cf89d9 --- /dev/null +++ b/fuzz/handlers/mach_message.cc @@ -0,0 +1,240 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/handlers/mach_message.h" + +#include +#include +#include +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "fuzz/host/host.h" +#include "fuzz/proto/common.pb.h" +#include "fuzz/proto/mach_message.pb.h" + +struct mach_msg_base_t { + mach_msg_header_t header; + mach_msg_body_t body; +} __attribute__((aligned(32))); + +mach_port_t GetMachPort(const MachPort &port) { + return (port.port() == PortNumberMach::CASE_NEGATIVE) ? 0xFFFFFFFFU + : port.port(); +} + +// TODO(nedwill): figure out how this is different from mach_port_t +mach_port_name_t GetMachPortName(const MachPortName &name) { + return GetMachPort(name.port()); +} + +mach_msg_type_name_t GetMachMsgTypeName(const MachMsgTypeName &type_name) { + return type_name; +} + +std::vector GetMachMsgPortDescriptor( + const MachMsgPortDescriptor &port_descriptor) { + mach_msg_port_descriptor_t result = { + .name = GetMachPort(port_descriptor.name()), + .disposition = GetMachMsgTypeName(port_descriptor.disposition()), + .type = MACH_MSG_PORT_DESCRIPTOR}; + + return {reinterpret_cast(&result), + reinterpret_cast(&result) + sizeof(result)}; +} + +mach_msg_copy_options_t GetMachMsgCopyOptions( + const MachMsgCopyOptions ©_options) { + return copy_options; +} + +std::vector GetMachMsgOolDescriptor( + const MachMsgOolDescriptor &ool_descriptor) { + mach_msg_ool_descriptor_t result = { + // TODO(nedwill): get OOL data from the message itself + .address = (void *)1, + .deallocate = ool_descriptor.deallocate(), + .copy = GetMachMsgCopyOptions(ool_descriptor.copy()), + .type = MACH_MSG_OOL_DESCRIPTOR, + .size = ool_descriptor.size()}; + + return {reinterpret_cast(&result), + reinterpret_cast(&result) + sizeof(result)}; +} + +std::vector GetMachMsgOolDescriptorVolatile( + const MachMsgOolDescriptor &ool_descriptor) { + mach_msg_ool_descriptor_t result = { + .address = (void *)1, + .deallocate = ool_descriptor.deallocate(), + .copy = GetMachMsgCopyOptions(ool_descriptor.copy()), + .type = MACH_MSG_OOL_VOLATILE_DESCRIPTOR, + .size = ool_descriptor.size()}; + + return {reinterpret_cast(&result), + reinterpret_cast(&result) + sizeof(result)}; +} + +std::vector GetMachMsgOolPortsDescriptor( + const MachMsgOolPortsDescriptor &ool_ports) { + mach_msg_ool_ports_descriptor_t result = { + // TODO(nedwill): pass ownership of names back up to caller so we can + // actually + // specify them in our test case + .address = (void *)1, // ool_ports.names.data(), + .deallocate = ool_ports.deallocate(), + .copy = GetMachMsgCopyOptions(ool_ports.copy()), + .disposition = GetMachMsgTypeName(ool_ports.disposition()), + .type = MACH_MSG_OOL_PORTS_DESCRIPTOR, + .count = static_cast(ool_ports.names().size()), + }; + + return {reinterpret_cast(&result), + reinterpret_cast(&result) + sizeof(result)}; +} + +std::vector GetMachMsgGuardedPortDescriptor( + const MachMsgGuardedPortDescriptor &guarded_port) { + mach_msg_guarded_port_descriptor_t result = { + .context = static_cast(guarded_port.context()), + .flags = static_cast(guarded_port.flags()), + .disposition = GetMachMsgTypeName(guarded_port.disposition()), + .type = MACH_MSG_GUARDED_PORT_DESCRIPTOR, + .name = GetMachPortName(guarded_port.name())}; + + return {reinterpret_cast(&result), + reinterpret_cast(&result) + sizeof(result)}; +} + +std::vector GetMachMsgDescriptor(const MachMsgDescriptor &descriptor) { + switch (descriptor.descriptor_case()) { + case MachMsgDescriptor::kPort: { + return GetMachMsgPortDescriptor(descriptor.port()); + } + case MachMsgDescriptor::kOutOfLine: { + return GetMachMsgOolDescriptor(descriptor.out_of_line()); + } + case MachMsgDescriptor::kOolPorts: { + return GetMachMsgOolPortsDescriptor(descriptor.ool_ports()); + } + case MachMsgDescriptor::kGuardedPort: { + return GetMachMsgGuardedPortDescriptor(descriptor.guarded_port()); + } + case MachMsgDescriptor::kOutOfLineVolatile: { + return GetMachMsgOolDescriptorVolatile(descriptor.out_of_line_volatile()); + } + case MachMsgDescriptor::DESCRIPTOR_NOT_SET: { + return {}; + } + } + return {}; +} + +std::vector GetMachMsgStruct( + mach_msg_type_name_t remote_type, mach_port_t remote_port, + mach_msg_type_name_t local_type, mach_port_t local_port, + mach_msg_type_name_t voucher_type, mach_port_t voucher_port, + mach_msg_id_t msgh_id, std::vector inline_data, + const std::vector> &descriptors) { + unsigned int other = 0; + size_t size = sizeof(mach_msg_header_t); + // printf("sizeof header %lu\n", size); + + if (!descriptors.empty()) { + // TODO(nedwill): consider fuzzing more options + other = MACH_MSGH_BITS_COMPLEX; + size += sizeof(mach_msg_body_t); + for (const auto &descriptor : descriptors) { + size += descriptor.size(); + } + } + // printf("base size %zu\n", size); + + mach_msg_header_t message_header = { + .msgh_bits = + MACH_MSGH_BITS_SET(remote_type, local_type, voucher_type, other), + .msgh_remote_port = remote_port, + .msgh_local_port = local_port, + .msgh_voucher_port = voucher_port, + .msgh_id = msgh_id}; + + // printf("inline data size %zu\n", inline_data.size()); + size += inline_data.size(); + + // Finally ready to update the size in the header + message_header.msgh_size = size; + + auto result = std::vector(size); + + size_t i = 0; + memcpy(result.data(), &message_header, sizeof(message_header)); + i += sizeof(message_header); + if (!descriptors.empty()) { + mach_msg_body_t body = { + .msgh_descriptor_count = + static_cast(descriptors.size())}; + memcpy(result.data() + i, reinterpret_cast(&body), sizeof(body)); + i += sizeof(body); + } + for (const auto &descriptor : descriptors) { + memcpy(result.data() + i, descriptor.data(), descriptor.size()); + i += descriptor.size(); + } + memcpy(result.data() + i, inline_data.data(), inline_data.size()); + i += inline_data.size(); + if (i != size) { + abort(); + } + return result; +} + +std::vector GetMachMsg(const MachMsg &msg) { + if (!msg.has_header()) { + return {msg.data().begin(), msg.data().end()}; + } + + const MachMsgHeader &header = msg.header(); + const MachMsgHeaderBits &bits = header.msgh_bits(); + + std::vector> descriptors; + for (const auto &descriptor : msg.body()) { + if (descriptor.descriptor_case() == MachMsgDescriptor::DESCRIPTOR_NOT_SET) { + continue; + } + descriptors.push_back(GetMachMsgDescriptor(descriptor)); + } + + return GetMachMsgStruct( + GetMachMsgTypeName(bits.remote()), GetMachPort(header.msgh_remote_port()), + GetMachMsgTypeName(bits.local()), GetMachPort(header.msgh_local_port()), + GetMachMsgTypeName(bits.voucher()), + GetMachPortName(header.msgh_voucher_port()), header.msgh_id(), + {msg.data().begin(), msg.data().end()}, descriptors); +} diff --git a/fuzz/handlers/mach_message.h b/fuzz/handlers/mach_message.h new file mode 100644 index 0000000..ac09fd8 --- /dev/null +++ b/fuzz/handlers/mach_message.h @@ -0,0 +1,73 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef MACH_TYPES_H_ +#define MACH_TYPES_H_ + +extern "C" { +#include +} + +#include +#include + +#include "fuzz/proto/mach_message.pb.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "fuzz/xnu/osfmk/api/types.h" +#include "third_party/bootstrap_cmds/mig_generated.pb.h" + +class MachMsg; +class MachMsgDescriptor; +class MachMsgOolDescriptor; +class MachMsgOolPortsDescriptor; +class MachMsgPortDescriptor; +class MachPort; +class MachPortName; +enum MachMsgTypeName : int; + +std::vector GetMachMsgDescriptor(const MachMsgDescriptor &descriptor); +mach_msg_type_name_t GetMachMsgTypeName(const MachMsgTypeName &type_name); +mach_port_t GetMachPort(const MachPort &port); +mach_port_name_t GetMachPortName(const MachPortName &name); +std::vector GetMachMsg(const MachMsg &msg); +std::vector GetMachMsgPortDescriptor( + const MachMsgPortDescriptor &port_descriptor); +std::vector GetMachMsgOolDescriptor( + const MachMsgOolDescriptor &ool_descriptor); +std::vector GetMachMsgOolPortsDescriptor( + const MachMsgOolPortsDescriptor &ool_ports); +std::vector GetMachMsgStruct( + mach_msg_type_name_t remote_type, mach_port_t remote_port, + mach_msg_type_name_t local_type, mach_port_t local_port, + mach_msg_type_name_t voucher_type, mach_port_t voucher_port, + mach_msg_id_t msgh_id, std::vector inline_data, + const std::vector> &descriptors); + +int HandleMachMsg2(const MachMsg2Trap &trap); + +#endif diff --git a/fuzz/handlers/mig_generated.h b/fuzz/handlers/mig_generated.h new file mode 100644 index 0000000..7f4400a --- /dev/null +++ b/fuzz/handlers/mig_generated.h @@ -0,0 +1,42 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef MIG_GENERATED_H_ +#define MIG_GENERATED_H_ + +#include + +#include + +#include "third_party/bootstrap_cmds/mig_generated.pb.h" + +std::vector GetMigRoutineMsgData( + const MigRoutine &routine, mach_msg_id_t *msgh_id, + std::vector> *descriptors); + +#endif \ No newline at end of file diff --git a/fuzz/handlers/mig_types.cc b/fuzz/handlers/mig_types.cc new file mode 100644 index 0000000..5cae894 --- /dev/null +++ b/fuzz/handlers/mig_types.cc @@ -0,0 +1,214 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/handlers/mig_types.h" + +#include +#include + +#include +#include + +#include "fuzz/handlers/mach_message.h" +#include "fuzz/handlers/mig_generated.h" +#include "fuzz/proto/mig_types.pb.h" +#include "fuzz/xnu/osfmk/api/backend.h" + +class MigRoutine; + +/* + * MIG supported protocols for Network Data Representation + */ +#define NDR_PROTOCOL_2_0 0 + +/* + * NDR 2.0 format flag type definition and values. + */ +#define NDR_INT_BIG_ENDIAN 0 +#define NDR_INT_LITTLE_ENDIAN 1 +#define NDR_FLOAT_IEEE 0 +#define NDR_FLOAT_VAX 1 +#define NDR_FLOAT_CRAY 2 +#define NDR_FLOAT_IBM 3 +#define NDR_CHAR_ASCII 0 +#define NDR_CHAR_EBCDIC 1 + +// mach_port_t GetTaskPort(const TaskPort &port) { +// // TODO(nedwill): support other tasks +// return task_self_trap_wrapper(); +// } + +#define VM_PROT_READ ((vm_prot_t)0x01) /* read permission */ +#define VM_PROT_WRITE ((vm_prot_t)0x02) /* write permission */ +#define VM_PROT_EXECUTE ((vm_prot_t)0x04) /* execute permission */ + +vm_prot_t GetVmProt(const VmProt &prot) { + vm_prot_t result = 0; + if (prot.read()) { + result |= VM_PROT_READ; + } + if (prot.write()) { + result |= VM_PROT_WRITE; + } + if (prot.execute()) { + result |= VM_PROT_EXECUTE; + } + return result; +} + +#define VM_SYNC_ASYNCHRONOUS ((vm_sync_t)0x01) +#define VM_SYNC_SYNCHRONOUS ((vm_sync_t)0x02) +#define VM_SYNC_INVALIDATE ((vm_sync_t)0x04) +#define VM_SYNC_KILLPAGES ((vm_sync_t)0x08) +#define VM_SYNC_DEACTIVATE ((vm_sync_t)0x10) +#define VM_SYNC_CONTIGUOUS ((vm_sync_t)0x20) +#define VM_SYNC_REUSABLEPAGES ((vm_sync_t)0x40) + +vm_sync_t GetVmSync(const VmSync &sync) { + vm_sync_t result = 0; + + if (sync.asynchronous()) { + result |= VM_SYNC_ASYNCHRONOUS; + } + if (sync.synchronous()) { + result |= VM_SYNC_SYNCHRONOUS; + } + if (sync.invalidate()) { + result |= VM_SYNC_INVALIDATE; + } + if (sync.killpages()) { + result |= VM_SYNC_KILLPAGES; + } + if (sync.deactivate()) { + result |= VM_SYNC_DEACTIVATE; + } + if (sync.contiguous()) { + result |= VM_SYNC_CONTIGUOUS; + } + if (sync.reusablepages()) { + result |= VM_SYNC_REUSABLEPAGES; + } + + return result; +} + +vm_behavior_t GetVmBehavior(const VmBehavior &behavior) { + return behavior; +} + +std::vector GetMigRoutineMsg(const MigRoutine &routine, + mach_msg_type_name_t remote_type, + mach_port_name_t remote_port, + mach_msg_type_name_t local_type, + mach_port_name_t local_port, + mach_msg_type_name_t voucher_type, + mach_port_name_t voucher_port) { + mach_msg_id_t msgh_id = 0; + std::vector> descriptors; + std::vector data = + GetMigRoutineMsgData(routine, &msgh_id, &descriptors); + if (!msgh_id) { + return {}; + } + return GetMachMsgStruct(remote_type, remote_port, local_type, local_port, + voucher_type, voucher_port, msgh_id, data, + descriptors); +} + +mach_port_qos_t GetMachPortQos(const MachPortQos &qos) { + mach_port_qos_t result = { + .name = qos.name(), + .prealloc = qos.prealloc(), + .len = static_cast(qos.len()), + }; + return result; +} + +mach_timespec_t GetMachTimespec(const MachTimespec ×pec) { + mach_timespec_t result = { + .tv_sec = static_cast(timespec.a1()), + .tv_nsec = static_cast(timespec.a2()), + }; + return result; +} + +mach_zone_name_t GetMachZoneName(const MachZoneName &zone_name) { + mach_zone_name_t result; + size_t size = zone_name.mzn_name().size(); + if (size > sizeof(result.mzn_name)) { + size = sizeof(result.mzn_name); + } + memcpy(result.mzn_name, zone_name.mzn_name().data(), size); + return result; +} + +dyld_kernel_image_info GetDyldKernelImageInfo(const DyldKernelImageInfo &info) { + dyld_kernel_image_info result = {}; + size_t size = info.uuid().size(); + if (size > sizeof(result.uuid)) { + size = sizeof(result.uuid); + } + memcpy(&result.uuid, info.uuid().data(), size); + result.fsobjid.fid_objno = info.fs_obj_id().fid_objno(); + result.fsobjid.fid_generation = info.fs_obj_id().fid_generation(); + result.fsid[0] = info.fsid().id1(); + result.fsid[1] = info.fsid().id2(); + result.load_addr = info.load_addr(); + return result; +} + +audit_token_t GetAuditToken(const AuditToken &info) { + audit_token_t audit_token = {}; + size_t size = info.val().size(); + if (size > sizeof(audit_token)) { + size = sizeof(audit_token); + } + + memcpy(&audit_token.val[0], info.val().data(), size); + + return audit_token; +} + +security_token_t GetSecurityToken(const SecurityToken &token) { + security_token_t security_token = {static_cast(token.token1()), + static_cast(token.token2())}; + return security_token; +} + + +// extern NDR_record_t NDR_record; +// NDR_record_t NDR_record = { +// 0, /* mig_reserved */ +// 0, /* mig_reserved */ +// 0, /* mig_reserved */ +// NDR_PROTOCOL_2_0, +// NDR_INT_LITTLE_ENDIAN, +// NDR_CHAR_ASCII, +// NDR_FLOAT_IEEE, +// 0, +// }; \ No newline at end of file diff --git a/fuzz/handlers/mig_types.h b/fuzz/handlers/mig_types.h new file mode 100644 index 0000000..4f2fad4 --- /dev/null +++ b/fuzz/handlers/mig_types.h @@ -0,0 +1,87 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef MIG_TYPES_H_ +#define MIG_TYPES_H_ + +extern "C" { +#include "fuzz/xnu/osfmk/api/types.h" +#include "third_party/bootstrap_cmds/include/mach/message.h" +} + +#include +#include + +#include "fuzz/proto/mig_types.pb.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "third_party/bootstrap_cmds/mig_generated.pb.h" + +class AuditToken; +class DyldKernelImageInfo; +class MachPortQos; +class MachTimespec; +class MachZoneName; +class MigRoutine; +class SecurityToken; +class TaskPort; +class VmProt; +class VmSync; +enum VmBehavior : int; + +typedef struct { + unsigned char mig_vers; + unsigned char if_vers; + unsigned char reserved1; + unsigned char mig_encoding; + unsigned char int_rep; + unsigned char char_rep; + unsigned char float_rep; + unsigned char reserved2; +} NDR_record_t; + +extern NDR_record_t NDR_record; + +// mach_port_t GetTaskPort(const TaskPort &port); +vm_prot_t GetVmProt(const VmProt &prot); +vm_sync_t GetVmSync(const VmSync &sync); +vm_behavior_t GetVmBehavior(const VmBehavior &behavior); +std::vector GetMigRoutineMsg(const MigRoutine &routine, + mach_msg_type_name_t remote_type, + mach_port_name_t remote_port, + mach_msg_type_name_t local_type, + mach_port_name_t local_port, + mach_msg_type_name_t voucher_type, + mach_port_name_t voucher_port); +mach_port_qos_t GetMachPortQos(const MachPortQos &qos); +mach_timespec_t GetMachTimespec(const MachTimespec ×pec); +mach_zone_name_t GetMachZoneName(const MachZoneName &zone_name); +dyld_kernel_image_info GetDyldKernelImageInfo(const DyldKernelImageInfo &info); +audit_token_t GetAuditToken(const AuditToken &info); +security_token_t GetSecurityToken(const SecurityToken &token); + +#endif diff --git a/fuzz/handlers/necp.cc b/fuzz/handlers/necp.cc new file mode 100644 index 0000000..679e5b0 --- /dev/null +++ b/fuzz/handlers/necp.cc @@ -0,0 +1,149 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +#include "bsd_types.h" +#include "fuzz/proto/bsd.pb.h" +#include "fuzz/proto/bsd_enums.pb.h" +#include "fuzz/proto/bsd_types.pb.h" + +typedef unsigned char uuid_t[16]; + +// Manually copied from syscall_wrappers.h as we can't mix XNU symbols with +// host symbols. +extern "C" int necp_client_action_wrapper(int necp_fd, uint32_t action, + uuid_t client_id, + size_t client_id_len, uint8_t *buffer, + size_t buffer_size, int *retval); + +std::string GetNecpClient(const NecpClientId &necp_client_id) { + switch (necp_client_id) { + case CLIENT_0: { + return "0000000000000000"; + } + case CLIENT_1: { + return "1111111111111111"; + } + case CLIENT_2: { + return "2222222222222222"; + } + } + assert(false); + return ""; +} + +void necp_client_add(int fd, NecpClientId client_id, unsigned char *data, + size_t size) { + std::string client_id_s = GetNecpClient(client_id); + int retval = 0; + necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_ADD, + // parameters + (unsigned char *)client_id_s.data(), + client_id_s.size(), data, size, &retval); +} + +// TODO(nedwill): support flow_ifnet_stats +void necp_client_remove(int fd, NecpClientId client_id) { + std::string client_id_s = GetNecpClient(client_id); + int retval = 0; + necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_REMOVE, + (unsigned char *)client_id_s.data(), + client_id_s.size(), nullptr, 0, &retval); +} + +void necp_client_copy_parameters(int fd, NecpClientId client_id, + uint32_t copyout_size) { + std::string client_id_s = GetNecpClient(client_id); + copyout_size %= 4096; + std::unique_ptr copyout_buffer(new uint8_t[copyout_size]); + int retval = 0; + necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_COPY_PARAMETERS, + (unsigned char *)client_id_s.data(), + client_id_s.size(), copyout_buffer.get(), + copyout_size, &retval); +} + +void necp_client_agent( + int fd, NecpClientId client_id, + const ::google::protobuf::RepeatedPtrField<::NecpTlv> &necp_tlv) { + std::string client_id_s = GetNecpClient(client_id); + std::string parameters; + for (const NecpTlv &tlv : necp_tlv) { + // std::string dat((char *)&icmp6_hdr, (char *)&icmp6_hdr + + // sizeof(icmp6_hdr)); + struct necp_tlv_header header = { + .type = (uint8_t)tlv.necp_type(), + .length = (uint32_t)tlv.data().size(), + }; + std::string tlv_s((char *)&header, (char *)&header + sizeof(header)); + tlv_s += tlv.data(); + parameters += tlv_s; + } + int retval = 0; + necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_AGENT, + (unsigned char *)client_id_s.data(), + client_id_s.size(), (uint8_t *)parameters.data(), + parameters.size(), &retval); +} + +void DoNecpClientAction(const NecpClientAction &necp_client_action) { + switch (necp_client_action.action_case()) { + case NecpClientAction::kAdd: { + necp_client_add(necp_client_action.necp_fd(), + necp_client_action.client_id(), + (unsigned char *)necp_client_action.add().buffer().data(), + necp_client_action.add().buffer().size()); + break; + } + case NecpClientAction::kRemove: { + necp_client_remove(necp_client_action.necp_fd(), + necp_client_action.client_id()); + break; + } + case NecpClientAction::kCopyParameters: { + necp_client_copy_parameters( + necp_client_action.necp_fd(), necp_client_action.client_id(), + necp_client_action.copy_parameters().copyout_size()); + break; + } + case NecpClientAction::kAgent: { + necp_client_agent(necp_client_action.necp_fd(), + necp_client_action.client_id(), + necp_client_action.agent().necp_tlv()); + break; + } + case NecpClientAction::ACTION_NOT_SET: { + break; + } + } +} diff --git a/fuzz/handlers/session.cc b/fuzz/handlers/session.cc new file mode 100644 index 0000000..a03850f --- /dev/null +++ b/fuzz/handlers/session.cc @@ -0,0 +1,131 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/handlers/session.h" + +#include "fuzz/host/host.h" +#include "fuzz/handlers/command_thread.h" +#include "fuzz/xnu/bsd/api/backend.h" + +void HandleSession(const Session *session) { + g_host->sync_tracker()->BeginSession(); + g_host->scheduler()->SetInterruptsEnabled(true); + + ready_for_preemption = false; + // CheckSchedulerState(); + + // size_t num_threads = g_host->scheduler->NumLiveThreads(); + + thread_t thread1 = CloneTaskFromInit(); + task_t task = GetTaskFromThread(thread1); + if (!thread1) { + abort(); + return; + } + + GetHypercallInterface()->ThreadSetContinuation(thread1, CommandThread, + (void *)&session->commands1()); + g_host->SetId(thread1, 1); + + thread_t thread2 = CreateWaitingThread(task); // CloneTaskFromInit(); + if (!thread2) { + abort(); + return; + } + + GetHypercallInterface()->ThreadSetContinuation(thread2, CommandThread, + (void *)&session->commands2()); + g_host->SetId(thread2, 2); + + // TODO(nedwill): figure out how to share send rights to task ports + thread_t thread3 = CloneTaskFromInit(); + task_t task2 = GetTaskFromThread(thread3); + if (thread3 == nullptr) { + abort(); + return; + } + + GetHypercallInterface()->ThreadSetContinuation(thread3, CommandThread, + (void *)&session->commands3()); + g_host->SetId(thread3, 3); + + // Now that we've setup the threads with continuations they are ready to run. + ClearTaskWait(task); + ClearTaskWait(task2); + + FuzzedDataProvider xnu_dp( + reinterpret_cast(session->xnu_data_provider().data()), + session->xnu_data_provider().size()); + xnu_fdp = &xnu_dp; + + g_host->scheduler()->SetRandSeed(session->schedule().rand_seed()); + FuzzedDataProvider thread_choices_dp( + reinterpret_cast( + session->schedule().thread_choices().data()), + session->schedule().thread_choices().size()); + g_host->scheduler()->SetThreadChoices(&thread_choices_dp); + ready_for_preemption = true; + + g_host->RunUntilEmpty(); + + // TODO(nedwill): generically abort and deallocate non-system threads + // TODO(nedwill): run these from another thread as they can get stuck + GetHypercallInterface()->ThreadAbort(thread1); + g_host->RunUntilEmpty(); + GetHypercallInterface()->ThreadAbort(thread2); + g_host->RunUntilEmpty(); + GetHypercallInterface()->ThreadAbort(thread3); + g_host->RunUntilEmpty(); + + thread_deallocate(thread1); + thread_deallocate(thread2); + thread_deallocate(thread3); + g_host->RunUntilEmpty(); + + g_host->Log("Killing all non init procs\n"); + g_host->scheduler()->SetInterruptsEnabled(false); + KillAllNonInitProcs(); + // Multiple calls might be needed if first call reparented ptraced children + while (XNUReapInitChildren()) { + } + + g_host->RunUntilEmpty(); + xnu_fdp = nullptr; + + g_host->NotifyEndOfSession(); + // TODO(nwach): Freeing everything causes sporadic crashes. Some must be + // meant to stick around in the kernel. + // g_adapter->sync_tracker()->FreeTracked(); + + // CheckSchedulerState(); + +#ifdef LIMIT_CALLGRIND_SCOPE + CALLGRIND_TOGGLE_COLLECT; + CALLGRIND_STOP_INSTRUMENTATION; +#endif +} diff --git a/fuzz/handlers/session.h b/fuzz/handlers/session.h new file mode 100644 index 0000000..25e299a --- /dev/null +++ b/fuzz/handlers/session.h @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_HANDLERS_SESSION_H_ +#define FUZZ_HANDLERS_SESSION_H_ + +#include "fuzz/proto/session.pb.h" + +void HandleSession(const Session *session); + +#endif // FUZZ_HANDLERS_SESSION_H_ diff --git a/fuzz/host/BUILD.bazel b/fuzz/host/BUILD.bazel new file mode 100644 index 0000000..821b87f --- /dev/null +++ b/fuzz/host/BUILD.bazel @@ -0,0 +1,176 @@ +load("@tools_deps//:requirements.bzl", "requirement") + +cc_library( + name = "host_interface", + hdrs = ["host_interface.h"], + deps = [ + ":logger_headers", + "//fuzz/host/hypercall:headers", + "//third_party/concurrence/sync", + ], +) + +cc_library( + name = "headers", + hdrs = [ + "host.h", + "hw_ticket_lock_manager.h", + ], + # TODO(nedwill): visibility should only be for sync_fakes + visibility = [ + "//fuzz/handlers:__pkg__", + "//fuzz/host/hypercall:__pkg__", + "//fuzz/xnu/osfmk:__pkg__", + ], + deps = [ + ":host_interface", + "//fuzz/executor:headers", + "//fuzz/host/hypercall:headers", + "//fuzz/xnu/osfmk:api_headers", + "//third_party/concurrence/scheduler:headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "mock_host", + hdrs = [ + "mock_host.h", + ], + deps = [":host_interface"], +) + +cc_library( + name = "hw_ticket_lock_manager", + srcs = ["hw_ticket_lock_manager.cc"], + hdrs = [ + "hw_ticket_lock_manager.h", + ], + deps = [ + ":headers", + ":host_interface", + "//third_party/concurrence/sync", + ], +) + +cc_test( + name = "hw_ticket_lock_manager_test", + size = "small", + srcs = ["hw_ticket_lock_manager_test.cc"], + deps = [ + ":hw_ticket_lock_manager", + ":logger", + ":mock_host", + "//third_party/concurrence/scheduler:mock_scheduler", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "logger_headers", + hdrs = [ + "logger.h", + ], + deps = [ + "@com_google_absl//absl/status:statusor", + ], +) + +cc_library( + name = "logger", + srcs = [ + "logger.cc", + ], + deps = [ + # For fuzz/host/hypercall/types.h + ":headers", + ":logger_headers", + "//fuzz/host/words", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + ], +) + +cc_test( + name = "logger_test", + size = "small", + srcs = ["logger_test.cc"], + deps = [ + ":logger", + "@com_google_googletest//:gtest_main", + ], +) + +# Integrates Host API with XNU +cc_library( + name = "host", + srcs = [ + "host.S", + "host.cc", + "//fuzz/xnu:libxnu.o", + ], + visibility = [ + "//fuzz/handlers:__pkg__", + "//fuzz/target:__pkg__", + ], + deps = [ + ":headers", + ":hw_ticket_lock_manager", + ":logger", + "//fuzz/executor:coroutine_executor", + "//fuzz/host/hypercall", + "//fuzz/xnu/bsd:headers", + "//third_party/concurrence/executor", + "//third_party/concurrence/scheduler:fuzzed_scheduler", + "//third_party/concurrence/sync", + ], +) + +# cc_test( +# name = "host_test", +# size = "small", +# srcs = [ +# "host_test.cc", +# ], +# deps = [ +# ":host", +# "//fuzz/handlers:mach_message", +# "//fuzz/host/hypercall", +# "//fuzz/xnu/test:headers", +# "@com_google_googletest//:gtest_main", +# ], +# ) + +cc_library( + name = "test_runner_lib", + srcs = ["test_runner.c"], + deps = [ + "@cmocka//:cmocka", + "//fuzz/host/hypercall:headers", + "//fuzz/xnu/osfmk:test_impl_header", + ], +) + +cc_test( + name = "unit_tests", + deps = [ + ":test_runner_lib", + "//fuzz/host:host", + "//fuzz/xnu/osfmk:test_impl", + ], +) + +py_test( + name = "validate_unit_test_sections_test", + srcs = ["validate_sections.py"], + main = "validate_sections.py", + args = [ + "$(location :unit_tests)", + ], + data = [":unit_tests"], + deps = [ + requirement("pyelftools"), + ], +) diff --git a/fuzz/host/host.S b/fuzz/host/host.S new file mode 100644 index 0000000..1aa3c3e --- /dev/null +++ b/fuzz/host/host.S @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +.globl GetStackPointer +.text +.type GetStackPointer,@function +GetStackPointer: + movq %rsp, %rax + ret + +.globl CallContinuationWithStack +.text +.type CallContinuationWithStack,@function +CallContinuationWithStack: + movq %rdi, %r12 /* continuation */ + movq %rsi, %r13 /* continuation param */ + movq %rdx, %r14 /* wait result */ + movq %rcx, %rsp /* set the stack */ + + xorq %rbp, %rbp /* zero frame pointer */ + + movq %r12, %rcx /* continuation */ + movq %r13, %rdi /* continuation param */ + movq %r14, %rsi /* wait result */ + + call *%rcx /* call continuation */ + + call GetHostThreadFinished + call *%rax /* Call ThreadFinished */ + + int3 /* This should now be unreachable */ diff --git a/fuzz/host/host.cc b/fuzz/host/host.cc new file mode 100644 index 0000000..dccf3aa --- /dev/null +++ b/fuzz/host/host.cc @@ -0,0 +1,677 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "host.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/hash/hash.h" +#include "absl/log/check.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_format.h" +#include "fuzz/executor/coroutine_executor.h" +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/xnu/bsd/api/backend.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "fuzz/xnu/osfmk/api/scheduler_state.h" +#include "fuzz/xnu/osfmk/api/thread.h" +#include "fuzz/xnu/osfmk/api/types.h" +#include "third_party/concurrence/backtrace/backtrace.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/fuzzed_scheduler.h" +#include "third_party/concurrence/sync/sync.h" +#include "third_party/concurrence/sync/tracker.h" + +thread_t main_thread; + +bool ready_for_preemption = false; +bool is_verbose = false; + +Host *g_host; + +enum { + THREAD_WAITING = -1, /* thread is waiting */ + THREAD_AWAKENED = 0, /* normal wakeup */ + THREAD_TIMED_OUT = 1, /* timeout expired */ + THREAD_INTERRUPTED = 2, /* aborted/interrupted */ + THREAD_RESTART = 3, /* restart operation entirely */ + THREAD_NOT_WAITING = 10 /* thread didn't need to wait */ +}; + +// First seven thread IDs are reserved for fuzzing/default threads +Host::Host(std::ostream *out) + : LoggerDetail(out), + scheduler_( + std::make_unique(new CoroutineExecutor, this)), + sync_tracker_(std::make_unique(scheduler_.get())), + hw_ticket_lock_manager_(this), + logger_(this), + next_thread_id_(7) {} + +ThreadHandle Host::ThreadTToHandle(thread_t thread) { + auto it = internal_to_handle_.find(thread); + if (it == internal_to_handle_.end()) { + return 0; + } + return it->second; +} + +ThreadHandle Host::CreateThread(thread_t thread, bool runnable) { + ThreadHandle handle = scheduler_->CreateThread( + [&]() { + ThreadStarted(); + __builtin_unreachable(); + }, + runnable); + +#ifndef NDEBUG + if (thread && internal_to_handle_.count(thread)) { + (*out_) << absl::StrFormat("Already have existing thread %p\n", thread); + abort(); + } + if (handle_to_internal_.count(handle)) { + abort(); + } +#endif + + internal_to_handle_[thread] = handle; + handle_to_internal_[handle] = thread; + return handle; +} + +void Host::Yield() { + if (!ready_for_preemption) { + return; + } + if (!scheduler_->GetInterruptsEnabled()) { + return; + } + + // We can't yield if we're already waiting on an event + thread_t thread = GetCurrentThreadT(); + if (IsWaiting(thread)) { + return; + } + + ThreadBlock(); +} + +void Host::SetContinuation(thread_t thread, thread_continue_t continuation, + void *param) { + if (!continuation) { + continuations_.erase(thread); + return; + } + + continuations_[thread] = {continuation, param}; +} + +void Host::CallContinuationIfPresent() { + thread_t thread = GetCurrentThreadT(); + + scheduler()->SetInterruptsEnabled(true); + auto it = continuations_.find(thread); + if (it == continuations_.end()) { + // No continuation present + return; + } + + auto [continuation, param] = it->second; + continuations_.erase(it); + uintptr_t sp = GetStackPointerForContinuation(); + wait_result_t wait_result = GetWaitResult(thread); + CallContinuationWithStack(continuation, param, wait_result, sp); +} + +__attribute__((noreturn)) void Host::SyscallReturn() { + auto it = saved_kernel_entry_contexts_.find(g_current_thread); + if (it == saved_kernel_entry_contexts_.end()) { + abort(); + } + __jmp_buf_tag *kernel_entry_context = it->second.get(); + longjmp(kernel_entry_context, 1); +} + +uint32_t Host::GetId(ThreadHandle handle) { + auto it = readable_ids_.find(handle); + if (it == readable_ids_.end()) { + uint32_t id = next_thread_id_++; + readable_ids_[handle] = id; + return id; + } + + return it->second; +} + +void Host::SetId(thread_t thread, uint32_t id) { + ThreadHandle handle = ThreadTToHandle(thread); + readable_ids_[handle] = id; + auto it = names_.find(handle); + if (it == names_.end()) { + names_[handle] = absl::StrFormat("user%d", id); + } +} + +void Host::SetThreadName(thread_t thread, const std::string &name) { + ThreadHandle handle = ThreadTToHandle(thread); + thread_set_thread_name(thread, name.c_str()); + scheduler_->SetThreadName(handle, name); + names_[handle] = name; +} + +static std::array thread_info; +std::string HostGetThreadInfo(thread_t thread) { + if (!thread) { + return ""; + } + XNUGetThreadInfo(thread, thread_info.data(), thread_info.size()); + return thread_info.data(); +} + +// \033 +const std::string reset_colors_and_text_effects = "\u001b[0m"; +const std::string default_colors = "\u001b[39m"; +const std::string bright_red = "\u001b[91m"; +const std::string bright_green = "\u001b[92m"; +const std::string bright_yellow = "\u001b[93m"; +const std::string bright_blue = "\u001b[94m"; +const std::string bright_magenta = "\u001b[95m"; +const std::string bright_cyan = "\u001b[96m"; +const std::string bright_white = "\u001b[97m"; + +const std::vector color_sequences = { + bright_magenta, bright_green, bright_red, bright_blue, + bright_yellow, bright_cyan, bright_white, +}; + +void Host::PrintThreadPrefix() { + uint32_t id = GetId(g_current_thread); + + (*out_) << absl::StrFormat( + "%s[%s]: ", color_sequences[(id - 1) % color_sequences.size()], + DescribeThreadHandle(g_current_thread)); +} + +void Host::ResetColors() { + (*out_) << reset_colors_and_text_effects; + (*out_).flush(); +} + +void Host::ThreadPrintf(const char *format, va_list args) { +#ifndef NDEBUG + if (!logger_.Enabled()) { + return; + } + + auto result = logger_.FormatWithPointers(format, args); + if (!result.ok()) { + logger_.Log(ANY_SUBSYSTEM, result.status().message()); + } + const std::string &formatted = result.value(); + + logger_.Log(ANY_SUBSYSTEM, formatted); + + if (formatted.empty() || formatted.back() != '\n') { + logger_.Log(ANY_SUBSYSTEM, "\n"); + } +#endif +} + +void Host::Log(absl::string_view string) { +#ifndef NDEBUG + if (!logger_.Enabled()) { + return; + } + PrintThreadPrefix(); + (*out_) << string; + ResetColors(); +#endif +} + +void Host::ThreadDestroyed(ThreadHandle handle) { + thread_t thread = HandleToThreadT(handle); + + internal_to_handle_.erase(thread); + handle_to_internal_.erase(handle); + saved_kernel_entry_contexts_.erase(handle); + saved_continuation_stack_pointers_.erase(handle); + continuations_.erase(thread); + copyin_blocked_.erase(handle); + suspended_.erase(thread); + terminating_.erase(thread); + waiting_.erase(thread); + wait_report_.erase(thread); + uninterruptible_.erase(thread); + enqueued_for_termination_.erase(thread); + wait_results_.erase(thread); +} + +void Host::NotifyEndOfSession() { + sync_tracker_->ReportSession(); +} + +void Host::EnableThreadPrintf() { + logger_.Enable(); +} + +thread_t Host::HandleToThreadT(ThreadHandle handle) { + auto it = handle_to_internal_.find(handle); + if (it == handle_to_internal_.end()) { + return nullptr; + abort(); + } + return it->second; +} + +thread_t Host::GetCurrentThreadT() { + return HandleToThreadT(g_current_thread); +} + +void Host::SetCurrentThreadT(thread_t thread) { + internal_to_handle_[thread] = g_current_thread; + handle_to_internal_[g_current_thread] = thread; +} + +__jmp_buf_tag *Host::SetKernelEntryPoint() { + // syscall_return means returning to the "userspace" stack. + // We save the current stack (below the userspace stack) so + // any continuations won't overwrite it. + SaveStackPointerForContinuation(); + + saved_kernel_entry_contexts_[g_current_thread] = + std::make_unique<__jmp_buf_tag>(); + __jmp_buf_tag *jmp_buf = saved_kernel_entry_contexts_[g_current_thread].get(); + assert(jmp_buf); + + // The setjmp man page says: + // "If the function which called setjmp() returns before longjmp() is + // called, the behavior is undefined. Some kind of subtle or unsubtle + // chaos is sure to result." + // But it should be okay in this case as we are careful to not use any + // stack variables and immediately return. + return jmp_buf; +} + +void Host::ThreadFinished() { + // Thread is going away + // logger_.Log(THREAD, "Host::ThreadFinished before terminate\n"); + XNUThreadTerminate(GetCurrentThreadT()); + // logger_.Log(THREAD, "Host::ThreadFinished after terminate\n"); + ast_taken_user(); +} + +void Host::SaveStackPointerForContinuation() { + // Weird hack but fetching this deeper in the call stack is fairly safe + // then we can continue to use it across continuations + uintptr_t sp = GetStackPointer() & ~0xFF; + saved_continuation_stack_pointers_[g_current_thread] = sp; +} + +uintptr_t Host::GetStackPointerForContinuation() { + // Subsequent continuations should reuse the same stack + auto it = saved_continuation_stack_pointers_.find(g_current_thread); + if (it != saved_continuation_stack_pointers_.end()) { + return it->second; + } + + SaveStackPointerForContinuation(); + return saved_continuation_stack_pointers_[g_current_thread]; +} + +void Host::BlockCopyin() { + copyin_blocked_[g_current_thread]++; +} + +void Host::UnblockCopyin() { + copyin_blocked_[g_current_thread]--; +} + +bool Host::IsCopyioBlocked() { + return copyin_blocked_[g_current_thread] > 0; +} + +std::string Host::DescribeThreadHandle(ThreadHandle handle) { + auto it = names_.find(handle); + if (it != names_.end()) { + return absl::StrFormat("%s", it->second); + } + return absl::StrFormat("kernel%d", GetId(handle)); +} + +// TODO(nedwill): move some of this logic to the scheduler. +// We only need to support the non-concurrence thread state externally. +void Host::ReportDeadlock(const std::deque &handles) { + for (ThreadHandle handle : handles) { + thread_t thread = HandleToThreadT(handle); + auto state = std::make_unique(); + GetThreadState(thread, state.get()); + + Sync *sync = sync_tracker_->GetObjectWithWaiter(handle); + if (!sync) { + continue; + } + (*out_) << absl::StrFormat( + "Thread 0x%lx (runnable %d waiting %d waitq %p) is blocked on sync " + "primitive %p\n", + handle, state->runnable, state->waiting, state->waitq, sync); + scheduler_->PrintBacktrace(handle); + for (const auto &it : sync->owner_backtraces()) { + (*out_) << "\nThe primitive was locked here:\n"; + it.second->Print(); + } + (*out_) << "\nThe primitive was constructed here:\n"; + sync->construction_stacktrace()->Print(); + } + abort(); +} + +void Host::ReportBadThreadState(enum bad_state state) { + switch (state) { + case RUNNING_AFTER_CONTINUATION: { + Log("Thread is resuming after continuation\n"); + abort(); + break; + } + } +} + +void Host::RunUntilEmpty() { + // TODO(nedwill): alternate scheduler implementations + while (ThreadHandle handle = scheduler_->ChooseThread(false)) { + SwitchTo(handle); + } + scheduler_->CleanupDeadThreads(); +} + +void Host::ThreadSetBlocked(thread_t thread) { + if (!IsEnqueuedForTermination(thread) && IsTerminating(thread)) { + SetEnqueuedForTermination(thread); + XNUThreadEnqueueTermination(thread); + } + + SetNotRunnable(thread); + + if (IsWaitReport(thread)) { + XNUThreadSchedCallNotifyBlocked(thread); + } +} + +void Host::SwitchFrom(thread_t thread) { + bool is_waiting = IsWaiting(thread); + if (is_waiting) { + ThreadSetBlocked(thread); + } else { + SetRunnable(thread); + } + + GetHypercallInterface()->ThreadSignalWake(thread); +} + +void Host::ThreadWillBlock() { + thread_t self = GetCurrentThreadT(); + SetWaiting(self); + SetUninterruptible(self); +} + +void Host::ThreadStarted() { + ThreadResumed(); +} + +void Host::ThreadResumed() { + thread_t self = GetCurrentThreadT(); + GetHypercallInterface()->ThreadClearWait(self, THREAD_AWAKENED); + CallContinuationIfPresent(); +} + +void Host::SwitchTo(ThreadHandle handle) { + thread_t current_thread = HandleToThreadT(g_current_thread); + + if (handle == g_current_thread) { + CallContinuationIfPresent(); + return; + } + + SwitchFrom(current_thread); + scheduler_->SwitchTo(handle); +} + +void Host::ThreadWakeup(thread_t thread, wait_result_t wait_result) { + GetHypercallInterface()->ThreadCancelWaitTimer(thread); + + bool wants_report = IsWaitReport(thread); + GetHypercallInterface()->ThreadClearWait(thread, wait_result); + + if (IsRunnable(thread) && !ShouldBlock(thread)) { + return; + } + + SetRunnable(thread); + ThreadHandle handle = ThreadTToHandle(thread); + scheduler_->MakeRunnable(handle); + + if (wants_report) { + GetHypercallInterface()->ThreadSchedCallNotifyUnblocked(thread); + } +} + +ThreadHandle Host::ThreadSelect() { + thread_t self = GetCurrentThreadT(); + bool still_running = scheduler_->IsRunnable(g_current_thread) && + !ShouldBlock(self) && !IsSuspended(self); + + ThreadHandle new_thread = scheduler_->ChooseThread(still_running); + if (new_thread) { + return new_thread; + } + +#ifndef NDEBUG + if (IsWaiting(main_thread)) { + Log("No runnable threads including main_thread\n"); + abort(); + } +#endif + + return scheduler_->GetMainThread(); +} + +bool Host::IsRunnable(thread_t thread) { + return scheduler_->IsRunnable(ThreadTToHandle(thread)); +} + +void Host::SetRunnable(thread_t thread) { + scheduler_->MakeRunnable(ThreadTToHandle(thread)); +} + +void Host::SetNotRunnable(thread_t thread) { + scheduler_->MakeNotRunnable(ThreadTToHandle(thread)); +} + +bool Host::IsSuspended(thread_t thread) { + return suspended_.count(thread) != 0; +} + +void Host::SetSuspended(thread_t thread) { + suspended_.insert(thread); + if (IsRunnable(thread)) { + suspended_runnable_.insert(thread); + SetNotRunnable(thread); + } +} + +void Host::SetNotSuspended(thread_t thread) { + suspended_.erase(thread); + if (suspended_runnable_.contains(thread)) { + suspended_runnable_.erase(thread); + SetRunnable(thread); + } +} + +bool Host::IsTerminating(thread_t thread) { + return terminating_.contains(thread); +} + +void Host::SetTerminating(thread_t thread) { + terminating_.insert(thread); +} + +bool Host::IsWaiting(thread_t thread) { + return waiting_.contains(thread); +} + +void Host::SetWaiting(thread_t thread) { + waiting_.insert(thread); +} + +void Host::SetNotWaiting(thread_t thread) { + waiting_.erase(thread); +} + +bool Host::IsWaitReport(thread_t thread) { + return wait_report_.contains(thread); +} + +void Host::SetWaitReport(thread_t thread) { + wait_report_.insert(thread); +} + +void Host::SetNotWaitReport(thread_t thread) { + wait_report_.erase(thread); +} + +bool Host::IsUninterruptible(thread_t thread) { + return uninterruptible_.contains(thread); +} + +void Host::SetUninterruptible(thread_t thread) { + uninterruptible_.insert(thread); +} + +void Host::SetNotUninterruptible(thread_t thread) { + uninterruptible_.erase(thread); +} + +bool Host::IsEnqueuedForTermination(thread_t thread) { + return enqueued_for_termination_.contains(thread); +} + +void Host::SetEnqueuedForTermination(thread_t thread) { + enqueued_for_termination_.insert(thread); +} + +void Host::SetNotEnqueuedForTermination(thread_t thread) { + enqueued_for_termination_.erase(thread); +} + +wait_result_t Host::GetWaitResult(thread_t thread) { + auto it = wait_results_.find(thread); + if (it == wait_results_.end()) { + return THREAD_WAITING; + } + return it->second; +} + +void Host::SetWaitResult(thread_t thread, wait_result_t wait_result) { + wait_results_[thread] = wait_result; +} + +bool Host::ShouldBlock(thread_t thread) { + return IsTerminating(thread) || IsWaiting(thread) || IsSuspended(thread); +} + +wait_result_t Host::ThreadBlock() { + thread_t self = GetCurrentThreadT(); + + ThreadHandle handle = ThreadSelect(); + SwitchTo(handle); + +#ifndef NDEBUG + if (IsWaiting(self)) { + CheckSchedulerState(); + Log("resumed a thread without clearing the waitq\n"); + abort(); + } +#endif + + return GetWaitResult(self); +} + +bool Host::IsHwTicketLockValid(VirtualMutex **tlock) { + return hw_ticket_lock_manager_.IsValid(tlock); +} + +void HandleSignal(int signal) { + auto stacktrace = std::make_unique(); + stacktrace->Print(); + _Exit(1); +} + +void InitializeHost() { +#if !__has_feature(address_sanitizer) + // Install signal handler + if (signal(SIGABRT, HandleSignal) == SIG_ERR) { + perror("signal"); + abort(); + } + if (signal(SIGSEGV, HandleSignal) == SIG_ERR) { + perror("signal"); + abort(); + } +#endif + + g_host = new Host(&std::cout); + if (is_verbose) { + g_host->EnableThreadPrintf(); + } + + // Temporarily set empty data provider during init + FuzzedDataProvider init_dp(nullptr, 0); + xnu_fdp = &init_dp; + g_host->scheduler()->SetThreadChoices(&init_dp); + g_host->scheduler()->SetRandSeed(0); + g_host->scheduler()->SetInterruptsEnabled(false); + + initialize_kernel(); + + CHECK_EQ(main_thread, g_host->GetCurrentThreadT()); + g_host->RunUntilEmpty(); + xnu_fdp = nullptr; +} diff --git a/fuzz/host/host.h b/fuzz/host/host.h new file mode 100644 index 0000000..8848a81 --- /dev/null +++ b/fuzz/host/host.h @@ -0,0 +1,226 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_HOST_HOST_H_ +#define FUZZ_HOST_HOST_H_ + +// Host integration code + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "fuzz/executor/coroutine_executor.h" +#include "fuzz/host/host_interface.h" +#include "fuzz/host/hw_ticket_lock_manager.h" +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/host/hypercall/types.h" +#include "fuzz/host/logger.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "fuzz/xnu/osfmk/api/types.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/scheduler.h" +#include "third_party/concurrence/sync/tracker.h" + +class FuzzedDataProvider; +class SyncTracker; +struct __jmp_buf_tag; + +// asm +extern "C" { +uintptr_t GetStackPointer(); +void CallContinuationWithStack(thread_continue_t continuation, void *parameter, + wait_result_t wresult, uintptr_t sp); +} + +extern FuzzedDataProvider *xnu_fdp; +class Host : public Scheduler::CallbackInterface, + public HostInterface, + public LoggerDetail { + public: + explicit Host(std::ostream *out); + ~Host() override = default; + Host(const Host &) = delete; + Host(Host &&) = delete; + Host &operator=(const Host &) = delete; + Host &operator=(Host &&) = delete; + + ThreadHandle CreateThread(thread_t thread, bool runnable); + + void Yield() override; + + // Thread state callbacks for Scheduler::CallbackInterface + void ThreadWillBlock() override; + void ThreadResumed() override; + void ThreadDestroyed(ThreadHandle handle) override; + std::string DescribeThreadHandle(ThreadHandle handle) override; + void ReportDeadlock(const std::deque &handles) override; + // TODO(nwach): should these be added to callback interface? + void ThreadStarted(); + void ThreadFinished(); + + void ThreadWakeup(thread_t thread, wait_result_t wait_result); + ThreadHandle ThreadSelect(); + + bool TimeoutThread(ThreadHandle handle); + + void Wait(ThreadHandle handle); + + void EnableThreadPrintf(); + void PrintThreadPrefix(); + void Log(absl::string_view string) override; + void ResetColors(); + // Helper for logging from C + void ThreadPrintf(const char *format, va_list args); + + void NotifyEndOfSession(); + + Scheduler *scheduler() { return scheduler_.get(); } + + SyncTracker *sync_tracker() override { return sync_tracker_.get(); } + + uint32_t GetId(ThreadHandle handle); + void SetId(thread_t thread, uint32_t id); + void SetThreadName(thread_t thread, const std::string &name); + + ThreadHandle ThreadTToHandle(thread_t thread); + thread_t HandleToThreadT(ThreadHandle handle); + + void SwitchFrom(thread_t thread); + void SwitchTo(ThreadHandle handle); + + thread_t GetCurrentThreadT() override; + void SetCurrentThreadT(thread_t thread); + + __jmp_buf_tag *SetKernelEntryPoint(); + + void SetContinuation(thread_t thread, thread_continue_t continuation, + void *param); + void CallContinuationIfPresent(); + __attribute__((noreturn)) void SyscallReturn(); + void SaveStackPointerForContinuation(); + uintptr_t GetStackPointerForContinuation(); + + void BlockCopyin(); + void UnblockCopyin(); + bool IsCopyioBlocked(); + + void ReportBadThreadState(enum bad_state state); + + void RunUntilEmpty(); + + bool IsRunnable(thread_t thread) override; + void SetRunnable(thread_t thread); + void SetNotRunnable(thread_t thread); + + bool IsSuspended(thread_t thread) override; + void SetSuspended(thread_t thread); + void SetNotSuspended(thread_t thread); + + bool IsTerminating(thread_t thread); + void SetTerminating(thread_t thread); + + bool IsWaiting(thread_t thread) override; + void SetWaiting(thread_t thread); + void SetNotWaiting(thread_t thread); + + bool IsWaitReport(thread_t thread); + void SetWaitReport(thread_t thread); + void SetNotWaitReport(thread_t thread); + + bool IsUninterruptible(thread_t thread); + void SetUninterruptible(thread_t thread); + void SetNotUninterruptible(thread_t thread); + + bool IsEnqueuedForTermination(thread_t thread); + void SetEnqueuedForTermination(thread_t thread); + void SetNotEnqueuedForTermination(thread_t thread); + + wait_result_t GetWaitResult(thread_t thread); + void SetWaitResult(thread_t thread, wait_result_t wait_result); + + void ThreadSetBlocked(thread_t thread); + bool ShouldBlock(thread_t thread); + void ThreadWait(thread_t thread, boolean_t until_not_runnable); + + wait_result_t ThreadBlock(); + + bool IsHwTicketLockValid(VirtualMutex **tlock); + + HwTicketLockManager *hw_ticket_lock_manager() { + return &hw_ticket_lock_manager_; + } + + Logger *logger() override { return &logger_; } + + private: + size_t GetRandomIndexForPointerName(); + std::string GenerateNameFromPointer(const void *ptr); + + std::unique_ptr scheduler_; + std::unique_ptr sync_tracker_; + HwTicketLockManager hw_ticket_lock_manager_; + Logger logger_; + // TODO(nedwill): drop all these maps and create a HostThread to track ThreadHandle/thread_t/extraneous data + absl::flat_hash_map readable_ids_; + absl::flat_hash_map names_; + absl::flat_hash_map internal_to_handle_; + absl::flat_hash_map handle_to_internal_; + absl::flat_hash_map> + saved_kernel_entry_contexts_; + absl::flat_hash_map + saved_continuation_stack_pointers_; + absl::flat_hash_map> + continuations_; + absl::flat_hash_map copyin_blocked_; + uint32_t next_thread_id_; + absl::flat_hash_set suspended_; + absl::flat_hash_set suspended_runnable_; + absl::flat_hash_set terminating_; + absl::flat_hash_set waiting_; + absl::flat_hash_set wait_report_; + absl::flat_hash_set uninterruptible_; + absl::flat_hash_set enqueued_for_termination_; + absl::flat_hash_map wait_results_; +}; + +extern Host *g_host; + +void InitializeHost(); + +#endif /* FUZZ_HOST_HOST_H_ */ diff --git a/fuzz/host/host_interface.h b/fuzz/host/host_interface.h new file mode 100644 index 0000000..fa3b4c5 --- /dev/null +++ b/fuzz/host/host_interface.h @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef HOST_INTERFACE_H_ +#define HOST_INTERFACE_H_ + +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "fuzz/host/hypercall/types.h" +#include "fuzz/host/logger.h" +#include "third_party/concurrence/sync/tracker.h" + +extern "C" { +#include "fuzz/xnu/osfmk/api/types.h" +} + +class HostInterface { + public: + virtual ~HostInterface() = default; + + virtual Logger *logger() = 0; + virtual SyncTracker *sync_tracker() = 0; + + virtual void Yield() = 0; + virtual bool IsRunnable(thread_t thread) = 0; + virtual bool IsSuspended(thread_t thread) = 0; + virtual bool IsWaiting(thread_t thread) = 0; + virtual thread_t GetCurrentThreadT() = 0; +}; + +#endif /* HOST_INTERFACE_H_ */ diff --git a/fuzz/host/host_test.cc b/fuzz/host/host_test.cc new file mode 100644 index 0000000..91ddce6 --- /dev/null +++ b/fuzz/host/host_test.cc @@ -0,0 +1,349 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/host.h" + +#include +#include + +#include + +#include "fuzz/handlers/mach_message.h" +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/proto/mach_message.pb.h" +#include "fuzz/xnu/bsd/api/backend.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "fuzz/xnu/osfmk/api/scheduler_state.h" +#include "fuzz/xnu/test/backend.h" + +extern int nprocs; +extern "C" int fork_wrapper(int *retval); + +class Environment : public ::testing::Environment { + public: + ~Environment() override = default; + + // Override this to define how to set up the environment. + void SetUp() override { + is_verbose = true; + InitializeHost(); + g_host->scheduler()->SetThreadChoices(nullptr); + } +}; + +testing::Environment *const env = + testing::AddGlobalTestEnvironment(new Environment); + +bool ThreadIsWaiting(thread_t thread) { + struct thread_debug_state state = {}; + GetThreadState(thread, &state); + return state.waiting; +} + +bool ThreadIsRunnable(thread_t thread) { + struct thread_debug_state state = {}; + GetThreadState(thread, &state); + return state.runnable; +} + +bool ThreadIsInterruptible(thread_t thread) { + struct thread_debug_state state = {}; + GetThreadState(thread, &state); + return !state.uninterruptible; +} + +uint64_t ThreadWaitEvent(thread_t thread) { + struct thread_debug_state state = {}; + GetThreadState(thread, &state); + return state.wait_event; +} + +bool ThreadIsTerminated(thread_t thread) { + struct thread_debug_state state = {}; + GetThreadState(thread, &state); + return state.terminate; +} + +bool ThreadIsTerminated2(thread_t thread) { + struct thread_debug_state state = {}; + GetThreadState(thread, &state); + return state.terminate2; +} + +void Waitq1(Waitq *waitq, wait_result_t /* unused */) { + waitq->AssertWait64(1, wait_interrupt::interruptible, 0); + GetHypercallInterface()->ThreadBlock(nullptr); + g_host->ThreadFinished(); +} + +void Waitq2(Waitq *waitq, wait_result_t /*unused*/) { + waitq->AssertWait64(1, wait_interrupt::interruptible, 0); + GetHypercallInterface()->ThreadBlock(nullptr); + g_host->ThreadFinished(); +} + +TEST(HostTest, WaitqWakeupTwoInterruptible) { + thread_t thread1 = CloneTaskFromInit(); + task_t task = GetTaskFromThread(thread1); + thread_t thread2 = CreateWaitingThread(task); + + Waitq waitq(sync_policy::fifo); + GetHypercallInterface()->ThreadSetContinuation( + thread1, reinterpret_cast(Waitq1), &waitq); + GetHypercallInterface()->ThreadSetContinuation( + thread2, reinterpret_cast(Waitq2), &waitq); + + ClearTaskWait(task); + + g_host->RunUntilEmpty(); + + EXPECT_FALSE(ThreadIsRunnable(thread1)); + EXPECT_TRUE(ThreadIsWaiting(thread1)); + EXPECT_TRUE(ThreadIsInterruptible(thread1)); + EXPECT_EQ(ThreadWaitEvent(thread1), 1); + + EXPECT_FALSE(ThreadIsRunnable(thread2)); + EXPECT_TRUE(ThreadIsWaiting(thread2)); + EXPECT_TRUE(ThreadIsInterruptible(thread2)); + EXPECT_EQ(ThreadWaitEvent(thread2), 1); + + // Wake up either thread1 or thread2 + kern_return ret = waitq.Wakeup64One(1, wait_result::interrupted, wakeup_flags::WAITQ_WAKEUP_DEFAULT); + EXPECT_EQ(ret, kern_return::success); + + // The scheduler implementation might change, so we shouldn't count on + // a certain order. All that matters is that exactly one of the + // threads is awake. There are only two possiblities... + if (ThreadIsRunnable(thread1)) { + // thread1 runnable + EXPECT_TRUE(ThreadIsRunnable(thread1)); + EXPECT_FALSE(ThreadIsWaiting(thread1)); + EXPECT_EQ(ThreadWaitEvent(thread1), 0); + + // thread2 waiting + EXPECT_FALSE(ThreadIsRunnable(thread2)); + EXPECT_TRUE(ThreadIsWaiting(thread2)); + EXPECT_EQ(ThreadWaitEvent(thread2), 1); + } else { + // thread1 waiting + EXPECT_FALSE(ThreadIsRunnable(thread1)); + EXPECT_TRUE(ThreadIsWaiting(thread1)); + EXPECT_EQ(ThreadWaitEvent(thread1), 1); + + // thread2 runnable + EXPECT_TRUE(ThreadIsRunnable(thread2)); + EXPECT_FALSE(ThreadIsWaiting(thread2)); + EXPECT_EQ(ThreadWaitEvent(thread2), 0); + } + + // Wake up the other one + ret = waitq.Wakeup64One(1, wait_result::interrupted, wakeup_flags::WAITQ_WAKEUP_DEFAULT); + EXPECT_EQ(ret, kern_return::success); + + // Thread 1 woke up + EXPECT_TRUE(ThreadIsRunnable(thread1)); + EXPECT_FALSE(ThreadIsWaiting(thread1)); + EXPECT_EQ(ThreadWaitEvent(thread1), 0); + + // Thread 2 woke up + EXPECT_TRUE(ThreadIsRunnable(thread2)); + EXPECT_FALSE(ThreadIsWaiting(thread2)); + EXPECT_EQ(ThreadWaitEvent(thread2), 0); + + ret = waitq.Wakeup64One(1, wait_result::interrupted, wakeup_flags::WAITQ_WAKEUP_DEFAULT); + EXPECT_EQ(ret, kern_return::not_waiting); + + // Threads haven't actually been run yet so haven't terminated + EXPECT_FALSE(ThreadIsTerminated(thread1)); + EXPECT_FALSE(ThreadIsTerminated(thread2)); + EXPECT_FALSE(ThreadIsTerminated2(thread1)); + EXPECT_FALSE(ThreadIsTerminated2(thread2)); + + g_host->RunUntilEmpty(); + + // Threads have finished running and terminated + EXPECT_FALSE(ThreadIsRunnable(thread1)); + EXPECT_FALSE(ThreadIsRunnable(thread2)); + EXPECT_TRUE(ThreadIsTerminated(thread1)); + EXPECT_TRUE(ThreadIsTerminated(thread2)); + EXPECT_TRUE(ThreadIsTerminated2(thread1)); + EXPECT_TRUE(ThreadIsTerminated2(thread2)); + + thread_deallocate(thread1); + thread_deallocate(thread2); + XNUReapInitChildren(); +} + +void ContinuationThread(void *ptr, wait_result_t wresult) { + ASSERT_EQ(static_cast(wresult), wait_result::awakened); + *reinterpret_cast(ptr) = true; + g_host->ThreadFinished(); +} + +void StartThread(void *ptr, wait_result_t /* unused */) { + GetHypercallInterface()->ThreadBlockReason(static_cast(ContinuationThread), + ptr); + ASSERT_TRUE(false); +} + +TEST(HostTest, ThreadBlockContinuation) { + bool continuation_executed = false; + thread_t thread = CloneTaskFromInit(); + task_t task = GetTaskFromThread(thread); + GetHypercallInterface()->ThreadSetContinuation(thread, + reinterpret_cast(StartThread), + &continuation_executed); + ClearTaskWait(task); + + g_host->RunUntilEmpty(); + EXPECT_TRUE(continuation_executed); + + thread_deallocate(thread); + g_host->RunUntilEmpty(); + XNUReapInitChildren(); +} + +void YieldingThread(void * /* unused */, wait_result_t /* unused */) { + g_host->Yield(); + g_host->ThreadFinished(); +} + +TEST(HostTest, Yield) { + thread_t thread = CloneTaskFromInit(); + task_t task = GetTaskFromThread(thread); + GetHypercallInterface()->ThreadSetContinuation( + thread, reinterpret_cast(YieldingThread), nullptr); + + thread_t thread2 = CreateWaitingThread(task); + GetHypercallInterface()->ThreadSetContinuation( + thread2, reinterpret_cast(YieldingThread), nullptr); + + thread_t thread3 = CloneTaskFromInit(); + task_t task2 = GetTaskFromThread(thread3); + GetHypercallInterface()->ThreadSetContinuation( + thread3, reinterpret_cast(YieldingThread), nullptr); + + ClearTaskWait(task); + ClearTaskWait(task2); + + g_host->RunUntilEmpty(); + thread_deallocate(thread); + thread_deallocate(thread2); + thread_deallocate(thread3); + g_host->RunUntilEmpty(); + XNUReapInitChildren(); +} + +void ForkingThread(void * /* unused */, wait_result_t /* unused */) { + int retval; + fork_wrapper(&retval); + g_host->ThreadFinished(); +} + +TEST(HostTest, Fork) { + // Other tests might not reap their children + XNUReapInitChildren(); + int original_nprocs = nprocs; + thread_t thread = CloneTaskFromInit(); + task_t task = GetTaskFromThread(thread); + GetHypercallInterface()->ThreadSetContinuation( + thread, reinterpret_cast(ForkingThread), nullptr); + + ClearTaskWait(task); + g_host->RunUntilEmpty(); + thread_deallocate(thread); + g_host->RunUntilEmpty(); + + // Both threads should be finished and reapable. + XNUReapInitChildren(); + + ASSERT_EQ(original_nprocs, nprocs); +} + +void CreateUserThreadThread(void * /* unused */, wait_result_t /* unused */) { + mach_port_name_t port = task_self_trap_wrapper(); + + // There must be a better way to represent this, but GetMachMsgStruct + // looks complicated. This was pulled from gdb. + const int thread_create_from_user = 3411; + std::vector msg = + GetMachMsgStruct(FUZZED_MACH_MSG_TYPE_COPY_SEND, port, 0, 0, 0, 0, + thread_create_from_user, {}, {}); + + mach_msg_trap_wrapper((user_addr_t)msg.data(), 1, msg.size(), 0, 0, 0, 0, 0); + + g_host->ThreadFinished(); +} + +TEST(HostTest, CreateUserThread) { + GTEST_SKIP_("Known to be broken."); + + // Other tests might not reap their children + XNUReapInitChildren(); + int original_nprocs = nprocs; + thread_t thread = CloneTaskFromInit(); + task_t task = GetTaskFromThread(thread); + GetHypercallInterface()->ThreadSetContinuation( + thread, reinterpret_cast(CreateUserThreadThread), + nullptr); + + ClearTaskWait(task); + g_host->RunUntilEmpty(); + thread_deallocate(thread); + g_host->RunUntilEmpty(); + + // Both threads should be finished and reapable. + XNUReapInitChildren(); + + ASSERT_EQ(original_nprocs, nprocs); +} + +TEST(HostTest, NestedBlockCopyin) { + EXPECT_FALSE(g_host->IsCopyioBlocked()); + + g_host->BlockCopyin(); + EXPECT_TRUE(g_host->IsCopyioBlocked()); + + g_host->BlockCopyin(); + EXPECT_TRUE(g_host->IsCopyioBlocked()); + + g_host->UnblockCopyin(); + EXPECT_TRUE(g_host->IsCopyioBlocked()); + + g_host->UnblockCopyin(); + EXPECT_FALSE(g_host->IsCopyioBlocked()); +} + +TEST(HostTest, Kheap) { + KHeap allocator; + for (int i = 0; i < 64000; i += 1024) { + void *sa = allocator.alloc(i, 0); + EXPECT_NE(sa, nullptr); + allocator.free_addr(sa); + } +} diff --git a/fuzz/host/hw_ticket_lock_manager.cc b/fuzz/host/hw_ticket_lock_manager.cc new file mode 100644 index 0000000..39cf6b6 --- /dev/null +++ b/fuzz/host/hw_ticket_lock_manager.cc @@ -0,0 +1,160 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/hw_ticket_lock_manager.h" + +#include + +#include "fuzz/host/host_interface.h" +#include "fuzz/host/hypercall/types.h" +#include "fuzz/host/logger.h" +#include "third_party/concurrence/sync/mutex.h" +#include "third_party/concurrence/sync/tracker.h" + +void HwTicketLockManager::Allocate(VirtualMutex** lock) { + *lock = host_->sync_tracker()->AllocateMutex(); + host_->logger()->Log(TICKET_LOCK, "Allocate -> %p\n", lock); + live_locks_.insert(lock); +} + +void HwTicketLockManager::Invalidate(VirtualMutex** lock) { + host_->logger()->Log(TICKET_LOCK, "Invalidate %p\n", lock); + live_locks_.erase(lock); + invalidated_locks_.insert(lock); +} + +void HwTicketLockManager::Destroy(VirtualMutex** lock, bool sync) { + host_->logger()->Log(TICKET_LOCK, "Destroy %p, %d\n", *lock, sync); + live_locks_.erase(lock); + + // Wakeup any waiters in the invalid case so we don't UaF + if (sync && invalidated_locks_.count(lock)) { + WakeupWaiters(lock); + if (!(*lock)->waiters().empty()) { + abort(); + } + } + + invalidated_locks_.erase(lock); + host_->sync_tracker()->FreeMutex(*lock); +} + +void HwTicketLockManager::WakeupWaiters(VirtualMutex** lock) { + host_->logger()->Log(TICKET_LOCK, "WakeupWaiters %p\n", lock); + if ((*lock)->waiters().empty()) { + host_->logger()->Log(TICKET_LOCK, "no waiters for %p, returning\n", lock); + return; + } + + host_->logger()->Log(TICKET_LOCK, "Unlocking %p\n", lock); + if ((*lock)->Held()) { + (*lock)->Unlock(); + } + while (!(*lock)->waiters().empty()) { + host_->logger()->Log(TICKET_LOCK, "Has waiters, yielding %p\n", lock); + host_->Yield(); + } +} + +bool HwTicketLockManager::IsValid(VirtualMutex** lock) { + return live_locks_.count(lock); +} + +hw_lock_status_t HwTicketLockManager::Lock(VirtualMutex** lock, + bool allow_invalid, uint64_t timeout, + void* handler) { + host_->logger()->Log(TICKET_LOCK, "Lock %p %d\n", lock, allow_invalid); + bool const is_invalid = invalidated_locks_.count(lock); + if (is_invalid) { + host_->logger()->Log(TICKET_LOCK, "is_invalid case\n"); + if (allow_invalid) { + return HW_LOCK_INVALID; + } + return HW_LOCK_ACQUIRED; + } + + if (!live_locks_.count(lock)) { + host_->logger()->Log(TICKET_LOCK, "not live, returning invalid\n"); + return HW_LOCK_INVALID; + } + + // TODO(nedwill): flip a coin here and return HW_LOCK_CONTENDED + + host_->logger()->Log(TICKET_LOCK, "locking %p\n", *lock); + (*lock)->Lock(); + + if (invalidated_locks_.count(lock)) { + host_->logger()->Log(TICKET_LOCK, "now invalid, unlocking\n"); + (*lock)->Unlock(); + return HW_LOCK_INVALID; + } + + host_->logger()->Log(TICKET_LOCK, "acquired!\n"); + return HW_LOCK_ACQUIRED; +} + +bool HwTicketLockManager::TryLock(VirtualMutex** lock) { + host_->logger()->Log(TICKET_LOCK, "TryLock %p\n", lock); + if (!live_locks_.count(lock)) { + return false; + } + + return (*lock)->TryLock(); +} + +void HwTicketLockManager::Unlock(VirtualMutex** lock) { + host_->logger()->Log(TICKET_LOCK, "Unlock %p\n", lock); + if (!live_locks_.count(lock) && !invalidated_locks_.count(lock)) { + return; + } + + if (!(*lock)->Held()) { + return; + } + + (*lock)->Unlock(); + host_->logger()->Log( + TICKET_LOCK, + "unlocking ticket lock, before yield runnable %d suspended %d " + "waiting %d\n", + host_->IsRunnable(host_->GetCurrentThreadT()), + host_->IsSuspended(host_->GetCurrentThreadT()), + host_->IsWaiting(host_->GetCurrentThreadT())); +} + +bool HwTicketLockManager::Held(VirtualMutex** lock) { + if (!live_locks_.count(lock)) { + return false; + } + + if (invalidated_locks_.count(lock)) { + abort(); + } + + return (*lock)->Held(); +} diff --git a/fuzz/host/hw_ticket_lock_manager.h b/fuzz/host/hw_ticket_lock_manager.h new file mode 100644 index 0000000..84f2867 --- /dev/null +++ b/fuzz/host/hw_ticket_lock_manager.h @@ -0,0 +1,75 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_HOST_HW_TICKET_LOCK_MANAGER_H_ +#define FUZZ_HOST_HW_TICKET_LOCK_MANAGER_H_ + +#include + +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "host_interface.h" +#include "third_party/concurrence/sync/mutex.h" + +class HostInterface; +class VirtualMutex; + +enum hw_lock_status_t { + HW_LOCK_INVALID = -1, + HW_LOCK_CONTENDED = 0, + HW_LOCK_ACQUIRED = 1, +}; + +class HwTicketLockManager { + public: + explicit HwTicketLockManager(HostInterface* host) : host_(host) {} + ~HwTicketLockManager() = default; + + void Allocate(VirtualMutex** lock); + void Invalidate(VirtualMutex** lock); + void Destroy(VirtualMutex** lock, bool sync); + void WakeupWaiters(VirtualMutex** lock); + + bool IsValid(VirtualMutex** lock); + + hw_lock_status_t Lock(VirtualMutex** lock, bool allow_invalid, + uint64_t timeout, void* handler); + bool TryLock(VirtualMutex** lock); + void Unlock(VirtualMutex** lock); + + bool Held(VirtualMutex** lock); + + private: + HostInterface* host_; + absl::flat_hash_set live_locks_; + absl::flat_hash_set invalidated_locks_; +}; + +#endif /* FUZZ_HOST_HW_TICKET_LOCK_MANAGER_H_ */ diff --git a/fuzz/host/hw_ticket_lock_manager_test.cc b/fuzz/host/hw_ticket_lock_manager_test.cc new file mode 100644 index 0000000..e225b0c --- /dev/null +++ b/fuzz/host/hw_ticket_lock_manager_test.cc @@ -0,0 +1,143 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "hw_ticket_lock_manager.h" + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "fuzz/host/logger.h" +#include "fuzz/host/mock_host.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/mock_scheduler.h" +#include "third_party/concurrence/sync/tracker.h" +#include "gmock/gmock.h" + +using ::testing::ElementsAre; +using ::testing::Return; + +bool is_verbose = false; + +class FakeLoggerDetail : public LoggerDetail { + public: + FakeLoggerDetail() : LoggerDetail(nullptr) {} + + void Log(absl::string_view str) override {} +}; + +class HwTicketLockManagerTest : public ::testing::Test { + protected: + HwTicketLockManagerTest() + : manager(&host), tracker(&scheduler), logger(&detail) {} + + void SetUp() override { + EXPECT_CALL(host, logger()).WillRepeatedly(Return(&logger)); + EXPECT_CALL(host, sync_tracker()).WillRepeatedly(Return(&tracker)); + } + + MockHost host; + MockScheduler scheduler; + HwTicketLockManager manager; + + SyncTracker tracker; + FakeLoggerDetail detail; + Logger logger; +}; + +TEST_F(HwTicketLockManagerTest, Invalidate) { + VirtualMutex* lock = nullptr; + manager.Allocate(&lock); + + EXPECT_TRUE(manager.IsValid(&lock)); + + manager.Invalidate(&lock); + EXPECT_FALSE(manager.IsValid(&lock)); + + manager.Destroy(&lock, true); + EXPECT_FALSE(manager.IsValid(&lock)); +} + +TEST_F(HwTicketLockManagerTest, LockValid) { + VirtualMutex* lock = nullptr; + manager.Allocate(&lock); + + // Acquire lock on thread 1 + g_current_thread = 1; + EXPECT_EQ(manager.Lock(&lock, false, 0, nullptr), HW_LOCK_ACQUIRED); + EXPECT_TRUE(manager.Held(&lock)); + + EXPECT_CALL(scheduler, Block()).WillOnce([&]() { + // Prepare MakeAllRunnable call from scheduler + EXPECT_CALL(scheduler, MakeAllRunnable(ElementsAre(2))); + // Thread 1 releases while thread 2 is blocked + g_current_thread = 1; + manager.Unlock(&lock); + g_current_thread = 2; + return true; + }); + + g_current_thread = 2; + EXPECT_EQ(manager.Lock(&lock, false, 0, nullptr), HW_LOCK_ACQUIRED); + + EXPECT_TRUE(manager.Held(&lock)); +} + +TEST_F(HwTicketLockManagerTest, LockInvalid) { + VirtualMutex* lock = nullptr; + manager.Allocate(&lock); + + // Acquire lock on thread 1 + g_current_thread = 1; + hw_lock_status_t status = manager.Lock(&lock, false, 0, nullptr); + EXPECT_EQ(status, HW_LOCK_ACQUIRED); + EXPECT_TRUE(manager.Held(&lock)); + + EXPECT_CALL(scheduler, Block()).WillOnce([&]() { + // Prepare MakeAllRunnable call from scheduler + EXPECT_CALL(scheduler, MakeAllRunnable(ElementsAre(2))); + // Thread 1 releases while thread 2 is blocked + g_current_thread = 1; + EXPECT_TRUE(manager.IsValid(&lock)); + manager.Invalidate(&lock); + EXPECT_FALSE(manager.IsValid(&lock)); + manager.Unlock(&lock); + EXPECT_CALL(scheduler, MakeAllRunnable(ElementsAre())); + g_current_thread = 2; + return true; + }); + + EXPECT_TRUE(manager.IsValid(&lock)); + + g_current_thread = 2; + EXPECT_EQ(manager.Lock(&lock, false, 0, nullptr), HW_LOCK_INVALID); + EXPECT_FALSE(manager.IsValid(&lock)); + EXPECT_FALSE(manager.Held(&lock)); +} diff --git a/fuzz/host/hypercall/BUILD.bazel b/fuzz/host/hypercall/BUILD.bazel new file mode 100644 index 0000000..dcda7a4 --- /dev/null +++ b/fuzz/host/hypercall/BUILD.bazel @@ -0,0 +1,43 @@ +# Header-only library to expose interface to XNU +cc_library( + name = "headers", + hdrs = [ + "hypercall.h", + "interrupt.h", + "lock.h", + "thread.h", + "types.h", + ], + visibility = [ + "//fuzz/common:__pkg__", + "//fuzz/host:__pkg__", + "//fuzz/xnu:__subpackages__", + "//third_party/xnu:__pkg__", + ], + deps = [ + "//fuzz/xnu/osfmk:api_headers", + "//fuzz/xnu/osfmk:fake_headers", + "//third_party/concurrence/executor:headers", + ], +) + +cc_library( + name = "hypercall", + srcs = [ + "hypercall.cc", + ], + visibility = [ + "//:__pkg__", + "//fuzz:__subpackages__", + ], + deps = [ + ":headers", + "//fuzz/host:headers", + "//fuzz/xnu/bsd:headers", + "//fuzz/xnu/osfmk:api_headers", + "//third_party/concurrence/executor", + "//third_party/concurrence/scheduler:fuzzed_scheduler", + "//third_party/concurrence/sync", + "@com_google_absl//absl/strings:str_format", + ], +) diff --git a/fuzz/host/hypercall/hypercall.cc b/fuzz/host/hypercall/hypercall.cc new file mode 100644 index 0000000..3601dec --- /dev/null +++ b/fuzz/host/hypercall/hypercall.cc @@ -0,0 +1,655 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/hypercall/hypercall.h" + +#include + +#include +#include +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "fuzz/host/host.h" +#include "fuzz/host/hypercall/interrupt.h" +#include "fuzz/host/hypercall/lock.h" +#include "fuzz/host/hypercall/thread.h" +#include "fuzz/host/hypercall/types.h" +#include "fuzz/host/logger.h" +#include "fuzz/xnu/bsd/api/backend.h" +#include "fuzz/xnu/osfmk/api/thread.h" +#include "fuzz/xnu/osfmk/api/types.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/scheduler.h" +#include "third_party/concurrence/sync/tracker.h" + +class VirtualMutex; + +FuzzedDataProvider* xnu_fdp; + +namespace { + +void Initialize() { + InitializeHost(); +} + +__attribute__((noreturn)) void Abort() { + abort(); +} + +void Yield() { + g_host->Yield(); +} + +void Block() { + g_host->scheduler()->Block(); +} + +__attribute__((noreturn)) void SyscallReturn() { + g_host->SyscallReturn(); +} + +void ThreadPrintf(const char* format, ...) { +#ifndef NDEBUG + va_list args; + va_start(args, format); + g_host->ThreadPrintf(format, args); + va_end(args); +#endif +} + +void Log(enum log_subsystem subsystem, const char* format, ...) { +#ifndef NDEBUG + va_list args; + va_start(args, format); + GetHypercallInterface()->LogVaList(format, args); + va_end(args); +#endif +} + +void LogVaList(const char* format, va_list args) { +#ifndef NDEBUG + g_host->ThreadPrintf(format, args); +#endif +} + +bool SetInterruptsEnabled(bool enable) { + return g_host->scheduler()->SetInterruptsEnabled(enable); +} + +bool GetInterruptsEnabled() { + return g_host->scheduler()->GetInterruptsEnabled(); +} + +thread_t GetCurrentThreadT() { + return g_host->GetCurrentThreadT(); +} + +void SetCurrentThreadT(thread_t thread) { + g_host->SetCurrentThreadT(thread); +} + +void NotifyDestroyed(thread_t thread) { + ThreadHandle handle = g_host->ThreadTToHandle(thread); + g_host->scheduler()->ThreadDestroyed(handle); +} + +void PrintThreadState(thread_t thread) { + ThreadHandle handle = g_host->ThreadTToHandle(thread); + g_host->scheduler()->PrintThreadState(handle); +} + +void PrintBacktrace(thread_t thread) { + ThreadHandle handle = g_host->ThreadTToHandle(thread); + g_host->scheduler()->PrintBacktrace(handle); +} + +void BlockCopyin() { + g_host->BlockCopyin(); +} + +void UnblockCopyin() { + g_host->UnblockCopyin(); +} + +bool IsCopyioBlocked() { + return g_host->IsCopyioBlocked(); +} + +void ResetBytesNeeded() { + g_host->scheduler()->ResetBytesNeeded(); +} + +size_t BytesNeeded() { + return g_host->scheduler()->BytesNeeded(); +} + +size_t RandomDataRemaining() { + return g_host->scheduler()->RandomDataRemaining(); +} + +void ReportBadThreadState(enum bad_state state) { + return g_host->ReportBadThreadState(state); +} + +void CreateThread(thread_t thread, bool runnable) { + g_host->CreateThread(thread, runnable); +} + +// These are callbacks to let the C-based backend access the fuzzed input +// stream. +void GetFuzzedBytes(void* addr, size_t bytes) { + // If we didn't initialize the fdp just clear the bytes. + if (!xnu_fdp) { + memset(addr, 0, bytes); + return; + } + memset(addr, 0, bytes); + std::vector dat = xnu_fdp->ConsumeBytes(bytes); + if (dat.empty()) { + return; + } + + memcpy(addr, dat.data(), dat.size()); +} + +bool GetFuzzedBool() { + // If we didn't initialize the fdp just return false. + if (!xnu_fdp) { + return false; + } + return xnu_fdp->ConsumeBool(); +} + +uint32_t GetFuzzedUint32() { + return xnu_fdp->ConsumeIntegral(); +} + +unsigned int GetFuzzedUint32InRange(unsigned int low, unsigned int high) { + return xnu_fdp->ConsumeIntegralInRange(low, high); +} + +uint64_t GetFuzzedUint64() { + return xnu_fdp->ConsumeIntegral(); +} + +uint64_t GetFuzzedUint64InRange(uint64_t low, uint64_t high) { + return xnu_fdp->ConsumeIntegralInRange(low, high); +} + +unsigned int GetRemainingFuzzedBytes() { + return xnu_fdp->remaining_bytes(); +} + +void FreeVirtualMutex(void* mutex) { + auto** m = static_cast(mutex); + g_host->sync_tracker()->FreeMutex(*m); + *m = nullptr; +} + +void* CurrentProc() { + return XNUCurrentProc(); +} + +bool IsHwTicketLockValid(void* lck) { + VirtualMutex** lock = reinterpret_cast(lck); + return g_host->IsHwTicketLockValid(lock); +} + +// TODO(nedwill): kern_return_t from kern_return.h +#define KERN_SUCCESS 0 + +kern_return_t ThreadGo(thread_t thread, wait_result_t wresult) { + g_host->ThreadWakeup(thread, wresult); + return KERN_SUCCESS; +} + +wait_result_t ThreadBlockReason(thread_continue_t continuation, + void* parameter) { + thread_t self = g_host->GetCurrentThreadT(); + + GetHypercallInterface()->ThreadSetContinuation(self, continuation, parameter); + + return g_host->ThreadBlock(); +} + +wait_result_t ThreadGetWaitResult(thread_t thread) { + return g_host->GetWaitResult(thread); +} + +wait_result_t ThreadMarkWaitLocked(thread_t thread, + wait_interrupt_t interruptible_orig) { + return XNUThreadMarkWaitLocked(thread, interruptible_orig); +} + +boolean_t ThreadStop(thread_t thread, boolean_t until_not_runnable) { + // TODO(nedwill): reenable this after dealing with deadlock + return false; + g_host->logger()->Log(THREAD, "HostThreadStop %d\n", + g_host->GetId(g_host->ThreadTToHandle(thread))); + return XNUThreadStop(thread, until_not_runnable); +} + +void ThreadSetTerminating(thread_t thread) { + g_host->SetTerminating(thread); +} + +void ThreadUnstop(thread_t thread) { + // TODO(nedwill): reenable this after dealing with deadlock + return; + g_host->logger()->Log(THREAD, "HostThreadUnstop %d\n", + g_host->GetId(g_host->ThreadTToHandle(thread))); +#ifndef NDEBUG + // assert((thread->state & (TH_RUN | TH_WAIT | TH_SUSP)) != TH_SUSP); + g_host->logger()->Log(THREAD, "sus %d runnable %d waiting %d\n", + GetHypercallInterface()->ThreadIsSuspended(thread), + GetHypercallInterface()->ThreadIsRunnable(thread), + GetHypercallInterface()->ThreadIsWaiting(thread)); + // if (HostThreadIsSuspended(thread) && + // !(HostThreadIsRunnable(thread) || HostThreadIsWaiting(thread))) { + // abort(); + // } +#endif + + if (GetHypercallInterface()->ThreadIsSuspended(thread)) { + GetHypercallInterface()->ThreadSetNotSuspended(thread); + XNUThreadSignalWake(thread); + } +} + +void ThreadWait(thread_t thread, boolean_t until_not_runnable) { + XNUThreadWait(thread, until_not_runnable); +} + +bool ThreadIsRunnable(thread_t thread) { + return g_host->IsRunnable(thread); +} + +void ThreadSetUninterruptibleWait(thread_t thread) { + g_host->SetWaiting(thread); + g_host->SetUninterruptible(thread); +} + +bool ThreadIsIdle(thread_t thread) { + return false; +} + +bool ThreadIsWaiting(thread_t thread) { + return g_host->IsWaiting(thread); +} + +bool ThreadIsEnqueuedForTermination(thread_t thread) { + return g_host->IsEnqueuedForTermination(thread); +} + +void ThreadSetQueuedForTermination(thread_t thread) { + g_host->SetEnqueuedForTermination(thread); + XNUThreadEnqueueTermination(thread); +} + +bool ThreadIsUninterruptible(thread_t thread) { + return g_host->IsUninterruptible(thread); +} + +bool ThreadIsTerminating(thread_t thread) { + return g_host->IsTerminating(thread); +} + +bool ThreadIsSuspended(thread_t thread) { + return g_host->IsSuspended(thread); +} + +void ThreadSetSuspended(thread_t thread) { + g_host->logger()->Log(THREAD, "HostThreadSetSuspended %d\n", + g_host->GetId(g_host->ThreadTToHandle(thread))); + g_host->SetSuspended(thread); +} + +void ThreadSetNotSuspended(thread_t thread) { + g_host->logger()->Log(THREAD, "HostThreadSetNotSuspended %d\n", + g_host->GetId(g_host->ThreadTToHandle(thread))); + g_host->SetNotSuspended(thread); +} + +bool ThreadWantsWaitReport(thread_t thread) { + return g_host->IsWaitReport(thread); +} + +void ThreadSignalWake(thread_t thread) { + XNUThreadSignalWake(thread); +} + +void ThreadClearUninterruptibleWait(thread_t thread) { + g_host->SetNotWaiting(thread); + g_host->SetNotUninterruptible(thread); +} + +void ThreadClearWait(thread_t thread, wait_result_t result) { + g_host->SetNotWaiting(thread); + g_host->SetNotWaitReport(thread); + g_host->SetNotUninterruptible(thread); + g_host->SetWaitResult(thread, result); +} + +void ThreadSetWaitResult(thread_t thread, wait_result_t wait_result) { + g_host->SetWaitResult(thread, wait_result); +} + +void ThreadCancelWaitTimer(thread_t thread) { + XNUThreadCancelWaitTimer(thread); +} + +void ThreadSchedCallNotifyUnblocked(thread_t thread) { + XNUThreadSchedCallNotifyUnblocked(thread); +} + +void ThreadSetContinuation(thread_t thread, thread_continue_t continuation, + void* parameter) { + g_host->SetContinuation(thread, continuation, parameter); +} + +void ThreadSetRunnable(thread_t thread) { + g_host->SetRunnable(thread); +} + +void ThreadSetNotRunnable(thread_t thread) { + g_host->SetNotRunnable(thread); +} + +void ThreadClearContinuation(thread_t thread) { + GetHypercallInterface()->ThreadSetContinuation(thread, nullptr, nullptr); +} + +void ThreadAbort(thread_t thread) { + XNUThreadAbort(thread); +} + +void ThreadFinished() { + g_host->ThreadFinished(); +} + +void ThreadSetWaiting(thread_t thread) { + g_host->SetWaiting(thread); +} + +void ThreadSetNotWaiting(thread_t thread) { + g_host->SetNotWaiting(thread); +} + +void ThreadSetWaitReport(thread_t thread) { + g_host->SetWaitReport(thread); +} + +void ThreadSetNotUninterruptible(thread_t thread) { + g_host->SetNotUninterruptible(thread); +} + +void ThreadSetNotWaitReport(thread_t thread) { + g_host->SetNotWaitReport(thread); +} + +void ThreadSetUninterruptible(thread_t thread) { + g_host->SetUninterruptible(thread); +} + +wait_result_t ThreadBlock(thread_continue_t continuation) { + return ThreadBlockReason(continuation, nullptr); +} + +HyperMutex* VirtualMutexToHyperMutex(VirtualMutex* mutex) { + return reinterpret_cast(mutex); +} + +VirtualMutex* HyperMutexToVirtualMutex(HyperMutex* mutex) { + return reinterpret_cast(mutex); +} + +HyperMutex* AllocateMutex() { + return VirtualMutexToHyperMutex(g_host->sync_tracker()->AllocateMutex()); +} + +void FreeMutex(HyperMutex* mutex) { + g_host->sync_tracker()->FreeMutex(HyperMutexToVirtualMutex(mutex)); +} + +void AssertMutexHeld(HyperMutex* mutex) { + HyperMutexToVirtualMutex(mutex)->AssertHeld(); +} + +void MutexLock(HyperMutex* mutex) { + HyperMutexToVirtualMutex(mutex)->Lock(); +} + +bool MutexTryLock(HyperMutex* mutex) { + return HyperMutexToVirtualMutex(mutex)->TryLock(); +} + +void MutexUnlock(HyperMutex* mutex) { + HyperMutexToVirtualMutex(mutex)->Unlock(); +} + +VirtualMutex** HyperTicketLockToVirtualMutex(HyperTicketLock** htlock) { + return reinterpret_cast(htlock); +} + +void TicketLockAllocate(HyperTicketLock** htlock) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + g_host->hw_ticket_lock_manager()->Allocate(tlock); +} + +void TicketLockDestroy(HyperTicketLock** htlock, bool sync) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + g_host->hw_ticket_lock_manager()->Destroy(tlock, sync); +} + +int TicketLockLock(HyperTicketLock** htlock, bool allow_invalid, + uint64_t timeout, void* handler) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + return g_host->hw_ticket_lock_manager()->Lock(tlock, allow_invalid, timeout, + handler); +} + +bool TicketLockTryLock(HyperTicketLock** htlock) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + return g_host->hw_ticket_lock_manager()->TryLock(tlock); +} + +void TicketLockUnlock(HyperTicketLock** htlock) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + g_host->hw_ticket_lock_manager()->Unlock(tlock); +} + +void TicketLockInvalidate(HyperTicketLock** htlock) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + g_host->hw_ticket_lock_manager()->Invalidate(tlock); +} + +bool TicketLockHeld(HyperTicketLock** htlock) { + VirtualMutex** tlock = HyperTicketLockToVirtualMutex(htlock); + return g_host->hw_ticket_lock_manager()->Held(tlock); +} + +VirtualRwLock* HyperRwLockToVirtualRwLock(HyperRwLock* hyper_rw_lock) { + return reinterpret_cast(hyper_rw_lock); +} + +HyperRwLock* AllocateRwLock() { + return reinterpret_cast( + g_host->sync_tracker()->AllocateRwLock()); +} + +void FreeRwLock(HyperRwLock* hyper_rw_lock) { + g_host->sync_tracker()->FreeRwLock(HyperRwLockToVirtualRwLock(hyper_rw_lock)); +} + +void RwLockExclusiveToShared(HyperRwLock* hyper_rw_lock) { + HyperRwLockToVirtualRwLock(hyper_rw_lock)->ExclusiveToShared(); +} + +bool RwLockSharedToExclusive(HyperRwLock* hyper_rw_lock) { + return HyperRwLockToVirtualRwLock(hyper_rw_lock)->SharedToExclusive(); +} + +void RwLockLockExclusive(HyperRwLock* hyper_rw_lock) { + HyperRwLockToVirtualRwLock(hyper_rw_lock)->LockExclusive(); +} + +bool RwLockTryLockExclusive(HyperRwLock* hyper_rw_lock) { + return HyperRwLockToVirtualRwLock(hyper_rw_lock)->TryLockExclusive(); +} + +void RwLockLockShared(HyperRwLock* hyper_rw_lock) { + HyperRwLockToVirtualRwLock(hyper_rw_lock)->LockShared(); +} + +bool RwLockTryLockShared(HyperRwLock* hyper_rw_lock) { + return HyperRwLockToVirtualRwLock(hyper_rw_lock)->TryLockShared(); +} + +void RwLockUnlockExclusive(HyperRwLock* hyper_rw_lock) { + HyperRwLockToVirtualRwLock(hyper_rw_lock)->UnlockExclusive(); +} + +void RwLockUnlockShared(HyperRwLock* hyper_rw_lock) { + HyperRwLockToVirtualRwLock(hyper_rw_lock)->UnlockShared(); +} + +void RwLockUnlock(HyperRwLock* hyper_rw_lock) { + HyperRwLockToVirtualRwLock(hyper_rw_lock)->Unlock(); +} + +bool RwLockIsShared(HyperRwLock* hyper_rw_lock) { + return HyperRwLockToVirtualRwLock(hyper_rw_lock)->IsShared(); +} + +bool MutexIsOwned(HyperMutex* hyper_mutex) { + return HyperMutexToVirtualMutex(hyper_mutex)->IsOwned(); +} + +struct HypercallInterface hypercall_interface { + .Initialize = Initialize, .Abort = Abort, .Yield = Yield, .Block = Block, + .SetInterruptsEnabled = SetInterruptsEnabled, .ThreadPrintf = ThreadPrintf, + .Log = Log, .LogVaList = LogVaList, + .ReportBadThreadState = ReportBadThreadState, .BlockCopyin = BlockCopyin, + .UnblockCopyin = UnblockCopyin, .RandomDataRemaining = RandomDataRemaining, + .BytesNeeded = BytesNeeded, .SyscallReturn = SyscallReturn, + .IsCopyioBlocked = IsCopyioBlocked, + .GetInterruptsEnabled = GetInterruptsEnabled, + .GetFuzzedBytes = GetFuzzedBytes, .GetFuzzedBool = GetFuzzedBool, + .GetFuzzedUint32 = GetFuzzedUint32, + .GetFuzzedUint32InRange = GetFuzzedUint32InRange, + .GetFuzzedUint64 = GetFuzzedUint64, + .GetFuzzedUint64InRange = GetFuzzedUint64InRange, + .GetRemainingFuzzedBytes = GetRemainingFuzzedBytes, + .FreeVirtualMutex = FreeVirtualMutex, .CurrentProc = CurrentProc, + .IsHwTicketLockValid = IsHwTicketLockValid, .ThreadGo = ThreadGo, + .ThreadMarkWaitLocked = ThreadMarkWaitLocked, + .ThreadSetTerminating = ThreadSetTerminating, + .ThreadIsTerminating = ThreadIsTerminating, + .ThreadIsEnqueuedForTermination = ThreadIsEnqueuedForTermination, + .ThreadStop = ThreadStop, .ThreadUnstop = ThreadUnstop, + .ThreadClearUninterruptibleWait = ThreadClearUninterruptibleWait, + .ThreadWait = ThreadWait, .ThreadIsRunnable = ThreadIsRunnable, + .ThreadSetRunnable = ThreadSetRunnable, + .ThreadSetNotRunnable = ThreadSetNotRunnable, + .ThreadSetUninterruptibleWait = ThreadSetUninterruptibleWait, + .ThreadIsIdle = ThreadIsIdle, .ThreadIsWaiting = ThreadIsWaiting, + .ThreadIsUninterruptible = ThreadIsUninterruptible, + .ThreadIsSuspended = ThreadIsSuspended, + .ThreadWantsWaitReport = ThreadWantsWaitReport, + .ThreadSignalWake = ThreadSignalWake, .ThreadClearWait = ThreadClearWait, + .ThreadSetWaitResult = ThreadSetWaitResult, + .ThreadCancelWaitTimer = ThreadCancelWaitTimer, + .ThreadSchedCallNotifyUnblocked = ThreadSchedCallNotifyUnblocked, + .ThreadSetNotSuspended = ThreadSetNotSuspended, + .ThreadSetSuspended = ThreadSetSuspended, + .ThreadSetWaitReport = ThreadSetWaitReport, + .ThreadSetNotWaitReport = ThreadSetNotWaitReport, + .ThreadSetUninterruptible = ThreadSetUninterruptible, + .ThreadSetNotUninterruptible = ThreadSetNotUninterruptible, + .ThreadGetWaitResult = ThreadGetWaitResult, .ThreadBlock = ThreadBlock, + .ThreadBlockReason = ThreadBlockReason, + .ThreadSetContinuation = ThreadSetContinuation, + .ThreadClearContinuation = ThreadClearContinuation, + .ThreadAbort = ThreadAbort, .ThreadFinished = ThreadFinished, + .ThreadSetWaiting = ThreadSetWaiting, + .ThreadSetNotWaiting = ThreadSetNotWaiting, .CreateThread = CreateThread, + .GetCurrentThreadT = GetCurrentThreadT, + .SetCurrentThreadT = SetCurrentThreadT, .PrintBacktrace = PrintBacktrace, + .PrintThreadState = PrintThreadState, .NotifyDestroyed = NotifyDestroyed, + .AllocateMutex = AllocateMutex, .FreeMutex = FreeMutex, + .AssertMutexHeld = AssertMutexHeld, .MutexLock = MutexLock, + .MutexTryLock = MutexTryLock, .MutexUnlock = MutexUnlock, + .MutexIsOwned = MutexIsOwned, + .TicketLockAllocate = TicketLockAllocate, + .TicketLockDestroy = TicketLockDestroy, .TicketLockLock = TicketLockLock, + .TicketLockTryLock = TicketLockTryLock, .TicketLockUnlock = TicketLockUnlock, + .TicketLockInvalidate = TicketLockInvalidate, + .TicketLockHeld = TicketLockHeld, .AllocateRwLock = AllocateRwLock, + .FreeRwLock = FreeRwLock, .RwLockExclusiveToShared = RwLockExclusiveToShared, + .RwLockSharedToExclusive = RwLockSharedToExclusive, + .RwLockLockExclusive = RwLockLockExclusive, + .RwLockTryLockExclusive = RwLockTryLockExclusive, + .RwLockLockShared = RwLockLockShared, + .RwLockTryLockShared = RwLockTryLockShared, + .RwLockUnlockExclusive = RwLockUnlockExclusive, + .RwLockUnlockShared = RwLockUnlockShared, .RwLockUnlock = RwLockUnlock, + .RwLockIsShared = RwLockIsShared, +}; + +} // namespace + +struct HypercallInterface* GetHypercallInterface() { + return &hypercall_interface; +} + +const HostIsHwTicketLockValidFunc GetHostIsHwTicketLockValid() { + return GetHypercallInterface()->IsHwTicketLockValid; +} + +HostGetInterruptsEnabledFunc GetHostInterruptsEnabled() { + return GetHypercallInterface()->GetInterruptsEnabled; +} + +HostThreadFinishedFunc GetHostThreadFinished(void) { + return GetHypercallInterface()->ThreadFinished; +} + +#undef panic +extern "C" void panic(const char* str, ...) { + g_host->Log("Thread panicking the kernel :(\n"); + va_list panic_str_args; + + va_start(panic_str_args, str); + vfprintf(stderr, str, panic_str_args); + fprintf(stderr, "\n"); + va_end(panic_str_args); + abort(); +} diff --git a/fuzz/host/hypercall/hypercall.h b/fuzz/host/hypercall/hypercall.h new file mode 100644 index 0000000..bed9d0e --- /dev/null +++ b/fuzz/host/hypercall/hypercall.h @@ -0,0 +1,188 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_HOST_HYPERCALL_HYPERCALL_H_ +#define FUZZ_HOST_HYPERCALL_HYPERCALL_H_ + +// Host interface exposed to kernel code (libxnu.so) + +#include +#include +#include +#include + +#ifdef LIBXNU_BUILD +#include +#include +#include +#include +#ifdef MACH_KERNEL_PRIVATE +#include +#endif /* MACH_KERNEL_PRIVATE */ +#else +#include "fuzz/xnu/osfmk/api/types.h" +#include "fuzz/xnu/osfmk/api/backend.h" + +struct HyperMutex; +struct HyperRwLock; +struct HyperTicketLock; +#endif + +extern thread_t main_thread; + +extern bool kernel_booted; +extern bool ready_for_preemption; +extern bool is_verbose; + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fuzz/host/hypercall/types.h" + +// This is the "hypercall" interface to the concurrence scheduler from +// XNU. We want this to be as small as possible. + +struct HypercallInterface { + void (*Initialize)(void); + __attribute__((noreturn)) void (*Abort)(void); + void (*Yield)(void); + void (*Block)(void); + bool (*SetInterruptsEnabled)(bool enabled); + void (*ThreadPrintf)(const char *format, ...); + void (*Log)(enum log_subsystem subsystem, const char *format, ...); + void (*LogVaList)(const char *format, va_list args); + void (*ReportBadThreadState)(enum bad_state state); + void (*BlockCopyin)(void); + void (*UnblockCopyin)(void); + size_t (*RandomDataRemaining)(void); + size_t (*BytesNeeded)(void); + __attribute__((noreturn)) void (*SyscallReturn)(); + bool (*IsCopyioBlocked)(void); + bool (*GetInterruptsEnabled)(); + void (*GetFuzzedBytes)(void *addr, size_t bytes); + bool (*GetFuzzedBool)(void); + uint32_t (*GetFuzzedUint32)(); + uint32_t (*GetFuzzedUint32InRange)(uint32_t low, uint32_t high); + uint64_t (*GetFuzzedUint64)(); + uint64_t (*GetFuzzedUint64InRange)(uint64_t low, uint64_t high); + unsigned int (*GetRemainingFuzzedBytes)(void); + void (*FreeVirtualMutex)(void *mutex); + void *(*CurrentProc)(); + bool (*IsHwTicketLockValid)(void *lck); + + kern_return_t (*ThreadGo)(thread_t thread, wait_result_t wresult); + wait_result_t (*ThreadMarkWaitLocked)(thread_t thread, + wait_interrupt_t interruptible_orig); + void (*ThreadSetTerminating)(thread_t thread); + bool (*ThreadIsTerminating)(thread_t thread); + bool (*ThreadIsEnqueuedForTermination)(thread_t thread); + boolean_t (*ThreadStop)(thread_t thread, boolean_t until_not_runnable); + void (*ThreadUnstop)(thread_t thread); + void (*ThreadClearUninterruptibleWait)(thread_t thread); + void (*ThreadWait)(thread_t thread, boolean_t until_not_runnable); + bool (*ThreadIsRunnable)(thread_t thread); + void (*ThreadSetRunnable)(thread_t thread); + void (*ThreadSetNotRunnable)(thread_t thread); + void (*ThreadSetUninterruptibleWait)(thread_t thread); + bool (*ThreadIsIdle)(thread_t thread); + bool (*ThreadIsWaiting)(thread_t thread); + bool (*ThreadIsUninterruptible)(thread_t thread); + bool (*ThreadIsSuspended)(thread_t thread); + bool (*ThreadWantsWaitReport)(thread_t thread); + bool (*ThreadShouldBlock)(thread_t thread); + void (*ThreadSignalWake)(thread_t thread); + void (*ThreadClearWait)(thread_t thread, wait_result_t result); + void (*ThreadSetWaitResult)(thread_t thread, wait_result_t result); + void (*ThreadCancelWaitTimer)(thread_t thread); + void (*ThreadSchedCallNotifyUnblocked)(thread_t thread); + void (*ThreadSetNotSuspended)(thread_t thread); + void (*ThreadSetSuspended)(thread_t thread); + void (*ThreadSetWaitReport)(thread_t thread); + void (*ThreadSetNotWaitReport)(thread_t thread); + void (*ThreadSetUninterruptible)(thread_t thread); + void (*ThreadSetNotUninterruptible)(thread_t thread); + wait_result_t (*ThreadGetWaitResult)(thread_t thread); + + // Continuations + wait_result_t (*ThreadBlock)(thread_continue_t continuation); + wait_result_t (*ThreadBlockReason)(thread_continue_t continuation, + void *parameter); + void (*ThreadSetContinuation)(thread_t thread, thread_continue_t continuation, + void *parameter); + void (*ThreadClearContinuation)(thread_t thread); + void (*ThreadAbort)(thread_t thread); + void (*ThreadFinished)(); + void (*ThreadSetWaiting)(thread_t thread); + void (*ThreadSetNotWaiting)(thread_t thread); + + void (*CreateThread)(thread_t thread, bool runnable); + thread_t (*GetCurrentThreadT)(void); + void (*SetCurrentThreadT)(thread_t thread); + void (*PrintBacktrace)(thread_t thread); + void (*PrintThreadState)(thread_t thread); + void (*NotifyDestroyed)(thread_t thread); + + struct HyperMutex *(*AllocateMutex)(void); + void (*FreeMutex)(struct HyperMutex *mutex); + void (*AssertMutexHeld)(struct HyperMutex *mutex); + void (*MutexLock)(struct HyperMutex *sp); + bool (*MutexTryLock)(struct HyperMutex *sp); + void (*MutexUnlock)(struct HyperMutex *sp); + bool (*MutexIsOwned)(struct HyperMutex *sp); + + void (*TicketLockAllocate)(struct HyperTicketLock **tlock); + void (*TicketLockDestroy)(struct HyperTicketLock **tlock, bool sync); + int (*TicketLockLock)(struct HyperTicketLock **htlock, bool allow_invalid, + uint64_t timeout, void *handler); + bool (*TicketLockTryLock)(struct HyperTicketLock **tlock); + void (*TicketLockUnlock)(struct HyperTicketLock **tlock); + void (*TicketLockInvalidate)(struct HyperTicketLock **tlock); + bool (*TicketLockHeld)(struct HyperTicketLock **tlock); + + struct HyperRwLock *(*AllocateRwLock)(void); + void (*FreeRwLock)(struct HyperRwLock *rwlock); + void (*RwLockExclusiveToShared)(struct HyperRwLock *rwlock); + bool (*RwLockSharedToExclusive)(struct HyperRwLock *rwlock); + void (*RwLockLockExclusive)(struct HyperRwLock *rwlock); + bool (*RwLockTryLockExclusive)(struct HyperRwLock *rwlock); + void (*RwLockLockShared)(struct HyperRwLock *rwlock); + bool (*RwLockTryLockShared)(struct HyperRwLock *rwlock); + void (*RwLockUnlockExclusive)(struct HyperRwLock *rwlock); + void (*RwLockUnlockShared)(struct HyperRwLock *rwlock); + void (*RwLockUnlock)(struct HyperRwLock *rwlock); + bool (*RwLockIsShared)(struct HyperRwLock *rwlock); +} __attribute__((aligned(128))); + +struct HypercallInterface* GetHypercallInterface(void); + +#ifdef __cplusplus +} +#endif + +#endif /* FUZZ_HOST_HYPERCALL_HYPERCALL_H_ */ diff --git a/fuzz/host/hypercall/interrupt.h b/fuzz/host/hypercall/interrupt.h new file mode 100644 index 0000000..885c98a --- /dev/null +++ b/fuzz/host/hypercall/interrupt.h @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_HOST_INTERRUPT_LOCK_H_ +#define FUZZ_HOST_INTERRUPT_LOCK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool (*HostGetInterruptsEnabledFunc)(); +HostGetInterruptsEnabledFunc GetHostInterruptsEnabled(); + +#ifdef __cplusplus +} +#endif + +#endif /* FUZZ_HOST_INTERRUPT_LOCK_H_ */ diff --git a/fuzz/host/hypercall/lock.h b/fuzz/host/hypercall/lock.h new file mode 100644 index 0000000..291731f --- /dev/null +++ b/fuzz/host/hypercall/lock.h @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_HOST_HYPERCALL_LOCK_H_ +#define FUZZ_HOST_HYPERCALL_LOCK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool (*HostIsHwTicketLockValidFunc)(void *hwlock); +const HostIsHwTicketLockValidFunc GetHostIsHwTicketLockValid(); + +#ifdef __cplusplus +} +#endif + +#endif /* FUZZ_HOST_HYPERCALL_LOCK_H_ */ diff --git a/fuzz/host/hypercall/thread.h b/fuzz/host/hypercall/thread.h new file mode 100644 index 0000000..367af12 --- /dev/null +++ b/fuzz/host/hypercall/thread.h @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_HOST_HYPERCALL_THREAD_H_ +#define FUZZ_HOST_HYPERCALL_THREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*HostThreadFinishedFunc)(); +HostThreadFinishedFunc GetHostThreadFinished(void); + +#ifdef __cplusplus +} +#endif + +#endif /* FUZZ_HOST_HYPERCALL_THREAD_H_ */ \ No newline at end of file diff --git a/fuzz/host/hypercall/types.h b/fuzz/host/hypercall/types.h new file mode 100644 index 0000000..9481e8c --- /dev/null +++ b/fuzz/host/hypercall/types.h @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_HOST_HYPERCALL_TYPES_H_ +#define FUZZ_HOST_HYPERCALL_TYPES_H_ + +enum bad_state { + RUNNING_AFTER_CONTINUATION = 0, +}; + +enum log_subsystem { + VIRTUAL_MEMORY = 0, + TICKET_LOCK = 1, + THREAD = 2, + ABORT = 3, + ANY_SUBSYSTEM = 4, +}; + +#endif /* FUZZ_HOST_HYPERCALL_TYPES_H_ */ diff --git a/fuzz/host/logger.cc b/fuzz/host/logger.cc new file mode 100644 index 0000000..2895971 --- /dev/null +++ b/fuzz/host/logger.cc @@ -0,0 +1,172 @@ +// Copyright 2024 Google LLC +// +// 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 "fuzz/host/logger.h" + +#include +#include + +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" +#include "absl/strings/numbers.h" +#include "fuzz/host/words/adjectives.h" +#include "fuzz/host/words/nouns.h" + +Logger::Logger(LoggerDetail* detail) + : detail_(detail), + pointer_names_prng_(1234) {} + +Logger::~Logger() = default; + +void Logger::Log(enum log_subsystem subsystem, absl::string_view string) { + if (subsystem == TICKET_LOCK) { + return; + } +#ifndef NDEBUG + detail_->Log(string); +#else + (void)detail_; +#endif +} + +std::string Logger::ReplacePointerPlaceholders(const char* format) { + return absl::StrReplaceAll(format, {{"%p", "[ptr:%p]"}}); +} + +absl::StatusOr>> Logger::ExtractPointers(const std::string& formatted) { + std::vector> pointers; + std::vector tokens = absl::StrSplit(formatted, absl::ByString("[ptr:")); + + for (size_t i = 1; i < tokens.size(); ++i) { + std::vector parts = absl::StrSplit(tokens[i], absl::MaxSplits(']', 1)); + if (parts.size() != 2) { + return absl::InvalidArgumentError(absl::StrFormat("Invalid pointer token: %s", tokens[i])); + } + + if (parts[0] == "(nil)") { + pointers.push_back(0); + } else { + try { + unsigned long long value; + if (absl::StartsWith(parts[0], "0x") || absl::StartsWith(parts[0], "0X")) { + value = std::stoull(parts[0], nullptr, 16); + } else { + value = std::stoull(parts[0], nullptr, 10); + } + + if (value <= std::numeric_limits::max()) { + pointers.push_back(static_cast(value)); + } else { + return absl::InvalidArgumentError(absl::StrFormat("Pointer value out of range: %s", parts[0])); + } + } catch (const std::exception& e) { + return absl::InvalidArgumentError(absl::StrFormat("Invalid pointer value: %s (%s)", parts[0], e.what())); + } + } + } + + return pointers; +} + +std::string Logger::FormatExtractedPointers(const std::string& formatted, const std::vector>& pointers) { + std::string result; + std::vector tokens = absl::StrSplit(formatted, absl::ByString("[ptr:")); + result = tokens[0]; + + for (size_t i = 0; i < pointers.size(); ++i) { + std::vector parts = absl::StrSplit(tokens[i + 1], absl::MaxSplits(']', 1)); + if (pointers[i].ok()) { + uintptr_t value = pointers[i].value(); + if (value == 0) { + result += "(nil)" + parts[1]; + } else { + result += absl::StrFormat("%#lx (%s)%s", static_cast(value), + GenerateNameFromPointer(reinterpret_cast(value)), + parts[1]); + } + } else { + result += "[ptr:" + tokens[i + 1]; + } + } + + return result; +} + +absl::StatusOr Logger::FormatWithPointers(const char* format, va_list args) { + // Replace all occurrences of %p with [ptr:%p] in the format string + std::string modified_format = ReplacePointerPlaceholders(format); + + // Use vsnprintf to format the string with the provided arguments + std::vector buffer(1024); // Start with a 1KB buffer + va_list args_copy; + va_copy(args_copy, args); + int result = vsnprintf(buffer.data(), buffer.size(), modified_format.c_str(), args_copy); + va_end(args_copy); + + if (result < 0) { + return absl::InvalidArgumentError("Error in vsnprintf"); + } + + if (static_cast(result) >= buffer.size()) { + // If the initial buffer was too small, resize it and try again + buffer.resize(result + 1); + va_copy(args_copy, args); + result = vsnprintf(buffer.data(), buffer.size(), modified_format.c_str(), args_copy); + va_end(args_copy); + + if (result < 0 || static_cast(result) >= buffer.size()) { + return absl::InvalidArgumentError("Error in vsnprintf after resize"); + } + } + + // Now that we have the formatted string, we can use FormatWithPointersForTesting + return FormatWithPointersForTesting(std::string(buffer.data(), result)); +} + +absl::StatusOr Logger::FormatWithPointersForTesting(const std::string& formatted) { + auto extracted_pointers = ExtractPointers(formatted); + if (!extracted_pointers.ok()) { + return extracted_pointers.status(); + } + + return FormatExtractedPointers(formatted, *extracted_pointers); +} + +size_t Logger::GetRandomIndexForPointerName() { + const size_t num_names = adjectives.size() * nouns.size(); + std::uniform_int_distribution<> distribution(0, num_names - 1); + return distribution(pointer_names_prng_); +} + +std::string Logger::GenerateNameFromPointer(const void* ptr) { + auto iter = pointer_names_.find(ptr); + if (iter != pointer_names_.end()) { + return iter->second; + } + + while (true) { + size_t index = GetRandomIndexForPointerName(); + std::string name = absl::StrFormat( + "%s_%s", adjectives[index % adjectives.size()], + absl::AsciiStrToLower( + nouns[(index / adjectives.size()) % nouns.size()])); + + if (pointer_names_used_.contains(name)) { + continue; + } + pointer_names_used_.insert(name); + pointer_names_[ptr] = name; + return name; + } +} \ No newline at end of file diff --git a/fuzz/host/logger.h b/fuzz/host/logger.h new file mode 100644 index 0000000..1997c42 --- /dev/null +++ b/fuzz/host/logger.h @@ -0,0 +1,101 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_HOST_LOG_H_ +#define FUZZ_HOST_LOG_H_ + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "fuzz/host/hypercall/types.h" + +class LoggerDetail { + public: + explicit LoggerDetail(std::ostream* out) : out_(out) {} + void SetOutputStream(std::ostream* out) { out_ = out; } + + virtual ~LoggerDetail() = default; + // Implementers must use the provided `out_` ostream to log the string. + virtual void Log(absl::string_view string) = 0; + + protected: + std::ostream* out_; +}; + +class Logger { + public: + explicit Logger(LoggerDetail* detail); + virtual ~Logger(); + + void Log(enum log_subsystem subsystem, absl::string_view string); + + template + void Log(enum log_subsystem subsystem, + const absl::FormatSpec& format, const Args&... args); + + absl::StatusOr FormatWithPointers(const char* format, + va_list args); + + // For testing purposes + absl::StatusOr FormatWithPointersForTesting(const std::string& formatted); + + void EnableSubsystem(enum log_subsystem subsystem) { + subsystems_[subsystem] = true; + } + + void DisableSubsystem(enum log_subsystem subsystem) { + subsystems_[subsystem] = false; + } + + void Enable() { enabled_ = true; } + + bool Enabled() const { return enabled_; } + + void Disable() { enabled_ = false; } + + private: + static std::string ReplacePointerPlaceholders(const char* format); + static absl::StatusOr>> ExtractPointers(const std::string& formatted); + std::string FormatExtractedPointers(const std::string& formatted, const std::vector>& pointers); + + size_t GetRandomIndexForPointerName(); + virtual std::string GenerateNameFromPointer(const void* ptr); + + LoggerDetail* detail_; + absl::flat_hash_map subsystems_{}; + bool enabled_{}; + std::mt19937 pointer_names_prng_; + absl::flat_hash_map pointer_names_; + absl::flat_hash_set pointer_names_used_; +}; + +template +void Logger::Log(enum log_subsystem subsystem, + const absl::FormatSpec& format, const Args&... args) { +#ifndef NDEBUG + if (!enabled_ || !subsystems_[subsystem]) { + return; + } + Log(subsystem, absl::StrFormat(format, args...)); +#endif +} + +#endif /* FUZZ_HOST_LOG_H_ */ diff --git a/fuzz/host/logger_test.cc b/fuzz/host/logger_test.cc new file mode 100644 index 0000000..ae87545 --- /dev/null +++ b/fuzz/host/logger_test.cc @@ -0,0 +1,67 @@ +// Copyright 2024 Google LLC +// +// 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 +#include +#include "fuzz/host/logger.h" + +class MockLogger : public Logger { +public: + explicit MockLogger(LoggerDetail* detail) : Logger(detail) {} + MOCK_METHOD(std::string, GenerateNameFromPointer, (const void* ptr), (override)); +}; + +class MockLoggerDetail : public LoggerDetail { +public: + explicit MockLoggerDetail(std::ostream* out) : LoggerDetail(out) {} + MOCK_METHOD(void, Log, (absl::string_view string), (override)); +}; + +TEST(LoggerTest, ExtractPointers) { + MockLoggerDetail detail(nullptr); + MockLogger logger(&detail); + + auto result = logger.FormatWithPointersForTesting("test [ptr:0x1] [ptr:(nil)] [ptr:0xFFFFFFFF]"); + ASSERT_TRUE(result.ok()); + EXPECT_THAT(*result, testing::HasSubstr("0x1")); + EXPECT_THAT(*result, testing::HasSubstr("(nil)")); + EXPECT_THAT(*result, testing::HasSubstr("0xffffffff")); + + result = logger.FormatWithPointersForTesting("invalid [ptr:not_a_pointer]"); + ASSERT_FALSE(result.ok()); +} + +TEST(LoggerTest, FormatExtractedPointers) { + MockLoggerDetail detail(nullptr); + MockLogger logger(&detail); + EXPECT_CALL(logger, GenerateNameFromPointer(reinterpret_cast(1))) + .WillOnce(testing::Return("one")); + EXPECT_CALL(logger, GenerateNameFromPointer(reinterpret_cast(0xFFFFFFFF))) + .WillOnce(testing::Return("max")); + + auto result = logger.FormatWithPointersForTesting("test [ptr:0x1] middle [ptr:(nil)] end [ptr:0xFFFFFFFF]"); + ASSERT_TRUE(result.ok()); + EXPECT_EQ(*result, "test 0x1 (one) middle (nil) end 0xffffffff (max)"); +} + +TEST(LoggerTest, FormatWithPointersIntegration) { + MockLoggerDetail detail(nullptr); + MockLogger logger(&detail); + EXPECT_CALL(logger, GenerateNameFromPointer(testing::_)) + .WillOnce(testing::Return("mock_name")); + + auto result = logger.FormatWithPointersForTesting("test [ptr:0x1234] middle [ptr:(nil)]"); + ASSERT_TRUE(result.ok()); + EXPECT_EQ(*result, "test 0x1234 (mock_name) middle (nil)"); +} \ No newline at end of file diff --git a/fuzz/host/mock_host.h b/fuzz/host/mock_host.h new file mode 100644 index 0000000..e4b9449 --- /dev/null +++ b/fuzz/host/mock_host.h @@ -0,0 +1,48 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef MOCK_HOST_H_ +#define MOCK_HOST_H_ + +#include "fuzz/host/host_interface.h" +#include "gmock/gmock.h" + +class MockHost : public HostInterface { + public: + MOCK_METHOD(Logger*, logger, (), (override)); + MOCK_METHOD(SyncTracker*, sync_tracker, (), (override)); + MOCK_METHOD(void, Yield, (), (override)); + MOCK_METHOD(bool, IsRunnable, (thread_t thread), (override)); + MOCK_METHOD(bool, IsSuspended, (thread_t thread), (override)); + MOCK_METHOD(bool, IsWaiting, (thread_t thread), (override)); + MOCK_METHOD(thread_t, GetCurrentThreadT, (), (override)); + + private: +}; + +#endif /* MOCK_HOST_H_ */ diff --git a/fuzz/host/test_runner.c b/fuzz/host/test_runner.c new file mode 100644 index 0000000..ef0e568 --- /dev/null +++ b/fuzz/host/test_runner.c @@ -0,0 +1,77 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include + +// Must come after system includes +#include + +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/xnu/osfmk/test/osfmk_test_impl.h" + +static void test_kalloc_type(void **state) { + (void)state; // Unused parameter + TestResult result = {0}; + test_kalloc_type_impl(&result); + + // Use cmocka assertions to report the test results + assert_int_equal(result.num_failures, 0); + + // If there were failures, print them and fail the test + if (result.num_failures > 0) { + for (int i = 0; i < result.num_failures; i++) { + print_error("%s\n", result.failure_messages[i]); + } + fail(); + } +} + +static int global_setup(void **state) { + (void)state; // Unused parameter + is_verbose = true; + GetHypercallInterface()->Initialize(); + return 0; +} + +static int global_teardown(void **state) { + (void)state; // Unused parameter + // Add any necessary cleanup here if possible in the future + return 0; +} + +int main(void) { + static const struct CMUnitTest tests[] = { + cmocka_unit_test(test_kalloc_type), + // Add more tests here as needed + }; + + return cmocka_run_group_tests(tests, global_setup, global_teardown); +} \ No newline at end of file diff --git a/fuzz/host/validate_sections.py b/fuzz/host/validate_sections.py new file mode 100644 index 0000000..c0420f4 --- /dev/null +++ b/fuzz/host/validate_sections.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# Copyright 2024 Google LLC +# +# 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. + +import sys +from elftools.elf.elffile import ELFFile + +def get_section_names(binary_path): + with open(binary_path, 'rb') as f: + elf = ELFFile(f) + return [section.name for section in elf.iter_sections()] + +def validate_section_name(name): + return ',' not in name and ' ' not in name and '"' not in name + +def main(binary_path): + section_names = get_section_names(binary_path) + invalid_sections = [name for name in section_names if not validate_section_name(name)] + + if invalid_sections: + print("Error: Invalid section names found:") + for name in invalid_sections: + print(f" - {name}") + return 1 + else: + print("All section names are valid.") + return 0 + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python validate_sections.py ") + sys.exit(1) + sys.exit(main(sys.argv[1])) \ No newline at end of file diff --git a/fuzz/host/words/BUILD.bazel b/fuzz/host/words/BUILD.bazel new file mode 100644 index 0000000..2c658c0 --- /dev/null +++ b/fuzz/host/words/BUILD.bazel @@ -0,0 +1,5 @@ +cc_library( + name = "words", + hdrs = ["adjectives.h", "nouns.h"], + visibility = ["//visibility:public"], +) diff --git a/fuzz/host/words/adjectives.h b/fuzz/host/words/adjectives.h new file mode 100644 index 0000000..553fc91 --- /dev/null +++ b/fuzz/host/words/adjectives.h @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Google LLC + * + * 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 +#include + +const std::vector adjectives = { + "admiring", "adoring", "affectionate", "agile", "amazing", "ambitious", "amused", + "brave", "bright", "charming", "clever", "cool", "compassionate", "competent", + "confident", "curious", "dazzling", "determined", "diligent", "eager", "energetic", + "enthusiastic", "friendly", "funny", "generous", "gentle", "happy", "harmonious", + "helpful", "imaginative", "jolly", "kind", "lively", "loving", "loyal", "nice", + "optimistic", "passionate", "patient", "peaceful", "plucky", "proud", "quirky", + "relaxed", "reliable", "silly", "sincere", "sympathetic", "thoughtful", "warmhearted", + "wise", "wonderful" +}; \ No newline at end of file diff --git a/fuzz/host/words/nouns.h b/fuzz/host/words/nouns.h new file mode 100644 index 0000000..45b0385 --- /dev/null +++ b/fuzz/host/words/nouns.h @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Google LLC + * + * 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 +#include + +const std::vector nouns = { + "albatross", "beaver", "cat", "dolphin", "elephant", "falcon", "giraffe", "hedgehog", + "iguana", "jaguar", "kangaroo", "lion", "monkey", "nightingale", "octopus", "penguin", + "quokka", "rabbit", "squirrel", "tiger", "unicorn", "vulture", "whale", "xenops", + "yak", "zebra" +}; \ No newline at end of file diff --git a/fuzz/net_fuzzer.cc b/fuzz/net_fuzzer.cc deleted file mode 100644 index 2b6e02f..0000000 --- a/fuzz/net_fuzzer.cc +++ /dev/null @@ -1,1046 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "net_fuzzer.pb.h" -#include "src/libfuzzer/libfuzzer_macro.h" - -extern "C" { -#include "api/backend.h" -#include "api/syscall_wrappers.h" -#include "types.h" -} - -/* -TODO(nedwill) - -Fuzz vsock domain -*/ - -// TODO(nedwill): support multiple addresses of each type below, -// not just one of each type -void get_in6_addr(struct in6_addr *sai, enum In6Addr addr) { - memset(sai, 0, sizeof(*sai)); - switch (addr) { - case IN6_ADDR_SELF: { - sai->__u6_addr.__u6_addr32[0] = 16810238; - sai->__u6_addr.__u6_addr32[0] = 0; - sai->__u6_addr.__u6_addr32[0] = 0; - sai->__u6_addr.__u6_addr32[0] = 16777216; - // assert(IN6_IS_ADDR_SELF(sai)); - break; - } - case IN6_ADDR_LINK_LOCAL: { - sai->s6_addr[0] = 0xfe; - sai->s6_addr[1] = 0x80; - // TODO(nedwill): set other fields? - assert(IN6_IS_ADDR_LINKLOCAL(sai)); - break; - } - case IN6_ADDR_LOOPBACK: { - *sai = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint8_t)addr}; - assert(IN6_IS_ADDR_LOOPBACK(sai)); - break; - } - case IN6_ADDR_REAL: - case MAYBE_LOCALHOST: { - *sai = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint8_t)addr}; - break; - } - case IN6_ADDR_V4COMPAT: { - sai->s6_addr[12] = 1; - assert(IN6_IS_ADDR_V4COMPAT(sai)); - break; - } - case IN6_ADDR_V4MAPPED: { - *(uint32_t *)&sai->s6_addr[8] = 0xffff0000; - assert(IN6_IS_ADDR_V4MAPPED(sai)); - break; - } - case IN6_ADDR_6TO4: { - sai->s6_addr16[0] = ntohs(0x2002); - assert(IN6_IS_ADDR_6TO4(sai)); - break; - } - case IN6_ADDR_LINKLOCAL: { - sai->s6_addr[0] = 0xfe; - sai->s6_addr[1] = 0x80; - assert(IN6_IS_ADDR_LINKLOCAL(sai)); - break; - } - case IN6_ADDR_SITELOCAL: { - sai->s6_addr[0] = 0xfe; - sai->s6_addr[1] = 0xc0; - assert(IN6_IS_ADDR_SITELOCAL(sai)); - break; - } - case IN6_ADDR_MULTICAST: { - sai->s6_addr[0] = 0xff; - assert(IN6_IS_ADDR_MULTICAST(sai)); - break; - } - case IN6_ADDR_UNIQUE_LOCAL: { - sai->s6_addr[0] = 0xfc; - assert(IN6_IS_ADDR_UNIQUE_LOCAL(sai)); - break; - } - case IN6_ADDR_MC_NODELOCAL: { - sai->s6_addr[0] = 0xff; - sai->s6_addr[1] = __IPV6_ADDR_SCOPE_NODELOCAL; - assert(IN6_IS_ADDR_MC_NODELOCAL(sai)); - break; - } - case IN6_ADDR_MC_INTFACELOCAL: { - sai->s6_addr[0] = 0xff; - sai->s6_addr[1] = __IPV6_ADDR_SCOPE_INTFACELOCAL; - assert(IN6_IS_ADDR_MC_INTFACELOCAL(sai)); - break; - } - case IN6_ADDR_MC_LINKLOCAL: { - sai->s6_addr[0] = 0xff; - sai->s6_addr[1] = __IPV6_ADDR_SCOPE_LINKLOCAL; - assert(IN6_IS_ADDR_MC_LINKLOCAL(sai)); - break; - } - case IN6_ADDR_MC_SITELOCAL: { - sai->s6_addr[0] = 0xff; - sai->s6_addr[1] = __IPV6_ADDR_SCOPE_SITELOCAL; - assert(IN6_IS_ADDR_MC_SITELOCAL(sai)); - break; - } - case IN6_ADDR_MC_ORGLOCAL: { - sai->s6_addr[0] = 0xff; - sai->s6_addr[1] = __IPV6_ADDR_SCOPE_ORGLOCAL; - assert(IN6_IS_ADDR_MC_ORGLOCAL(sai)); - break; - } - case IN6_ADDR_MC_GLOBAL: { - sai->s6_addr[0] = 0xff; - sai->s6_addr[1] = __IPV6_ADDR_SCOPE_GLOBAL; - assert(IN6_IS_ADDR_MC_GLOBAL(sai)); - break; - } - case IN6_ADDR_UNSPECIFIED: - case IN6_ADDR_ANY: { - assert(IN6_IS_ADDR_UNSPECIFIED(sai)); - break; - } - case IN6_ADDR_LOCAL_ADDRESS: { - // Discovered this address dynamically - // fe80:0001:0000:0000:a8aa:aaaa:aaaa:aaaa - sai->s6_addr16[0] = 0xfe80; - sai->s6_addr16[1] = 0x0001; - sai->s6_addr16[4] = 0xa8aa; - sai->s6_addr16[5] = 0xaaaa; - sai->s6_addr16[6] = 0xaaaa; - sai->s6_addr16[7] = 0xaaaa; - } - // *sai = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint8_t)addr}; - } -} - -void get_sockaddr6(struct sockaddr_in6 *sai, const SockAddr6 &sa6) { - sai->sin6_len = sizeof(struct sockaddr_in6); - sai->sin6_family = (sa_family_t)AF_INET6; // sa6.family(); - sai->sin6_port = (in_port_t)sa6.port(); - sai->sin6_flowinfo = sa6.flow_info(); - get_in6_addr(&sai->sin6_addr, sa6.sin6_addr()); - sai->sin6_scope_id = sa6.sin6_scope_id(); -} - -std::string get_sockaddr(const SockAddr &sockaddr) { - std::string dat; - switch (sockaddr.sockaddr_case()) { - case SockAddr::kSockaddrGeneric: { - const SockAddrGeneric &sag = sockaddr.sockaddr_generic(); - // data size + sizeof(sa_len) + sizeof(sa_family) - struct sockaddr_generic sag_s = { - .sa_len = (uint8_t)(sizeof(sockaddr_generic) + sag.sa_data().size()), - .sa_family = (uint8_t)sag.sa_family(), - }; - - dat = std::string((char *)&sag_s, (char *)&sag_s + sizeof(sag_s)); - dat += sag.sa_data(); - break; - } - case SockAddr::kSockaddr4: { - struct sockaddr_in sai = { - .sin_len = sizeof(struct sockaddr_in), - .sin_family = - AF_INET, // (unsigned char)sockaddr.sockaddr4().sin_family(), - .sin_port = (unsigned short)sockaddr.sockaddr4().sin_port(), - .sin_addr = {(unsigned int)sockaddr.sockaddr4().sin_addr()}, - .sin_zero = {}, - }; - dat = std::string((char *)&sai, (char *)&sai + sizeof(sai)); - break; - } - case SockAddr::kSockaddr6: { - struct sockaddr_in6 sai = {}; - get_sockaddr6(&sai, sockaddr.sockaddr6()); - dat = std::string((char *)&sai, (char *)&sai + sizeof(sai)); - break; - } - // case SockAddr::kRawBytes: { - // dat = sockaddr.raw_bytes(); - // break; - // } - case SockAddr::SOCKADDR_NOT_SET: { - break; - } - } - return dat; -} - -std::string get_ip6_hdr(const Ip6Hdr &hdr, uint16_t expected_size) { - struct ip6_hdr ip6_hdr; - memset(&ip6_hdr, 0, sizeof(ip6_hdr)); - get_in6_addr(&ip6_hdr.ip6_src, hdr.ip6_src()); - get_in6_addr(&ip6_hdr.ip6_dst, hdr.ip6_src()); - ip6_hdr.ip6_ctlun.ip6_un2_vfc = IPV6_VERSION; - // How TF does flow work? - // ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_flow = hdr.ip6_hdrctl().ip6_un1_flow(); - ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_plen = - __builtin_bswap16(expected_size); // hdr.ip6_hdrctl().ip6_un1_plen(); - ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_nxt = hdr.ip6_hdrctl().ip6_un1_nxt(); - ip6_hdr.ip6_ctlun.ip6_un1.ip6_un1_hlim = hdr.ip6_hdrctl().ip6_un1_hlim(); - std::string dat((char *)&ip6_hdr, (char *)&ip6_hdr + sizeof(ip6_hdr)); - return dat; -} - -std::string get_ip_hdr(const IpHdr &hdr, size_t expected_size) { - struct in_addr ip_src = {.s_addr = (unsigned int)hdr.ip_src()}; - struct in_addr ip_dst = {.s_addr = (unsigned int)hdr.ip_dst()}; - struct ip ip_hdr = { - .ip_hl = 5, // TODO(nedwill): support options // hdr.ip_hl(), - .ip_v = IPV4, // hdr.ip_v(), - .ip_tos = (u_char)hdr.ip_tos(), - .ip_len = (u_short)__builtin_bswap16(expected_size), - .ip_id = (u_short)hdr.ip_id(), - .ip_off = (u_short)hdr.ip_off(), - .ip_ttl = (u_char)hdr.ip_ttl(), - .ip_p = (u_char)hdr.ip_p(), - .ip_sum = 0, - .ip_src = ip_src, - .ip_dst = ip_dst, - }; - std::string dat((char *)&ip_hdr, (char *)&ip_hdr + sizeof(ip_hdr)); - return dat; -} - -// message TcpHdr { -// required Port th_sport = 1; -// required Port th_dport = 2; -// required uint32 th_seq = 3; -// required uint32 th_ack = 4; -// required uint32 th_off = 5; -// repeated TcpFlag th_flags = 6; -// required uint32 th_win = 7; -// required uint32 th_sum = 8; -// required uint32 th_urp = 9; -// } - -std::string get_tcp_hdr(const TcpHdr &hdr) { - struct tcphdr tcphdr = { - .th_sport = (unsigned short)hdr.th_sport(), - .th_dport = (unsigned short)hdr.th_dport(), - .th_seq = __builtin_bswap32(hdr.th_seq()), - .th_ack = __builtin_bswap32(hdr.th_ack()), - .th_off = hdr.th_off(), - .th_flags = 0, - .th_win = (unsigned short)hdr.th_win(), - .th_sum = 0, - .th_urp = (unsigned short)hdr.th_urp(), - }; - - for (const int flag : hdr.th_flags()) { - tcphdr.th_flags ^= flag; - } - - // Prefer pure syn - if (hdr.is_pure_syn()) { - tcphdr.th_flags &= ~(TH_RST | TH_ACK); - tcphdr.th_flags |= TH_SYN; - } else if (hdr.is_pure_ack()) { - tcphdr.th_flags &= ~(TH_RST | TH_SYN); - tcphdr.th_flags |= TH_ACK; - } - - std::string dat((char *)&tcphdr, (char *)&tcphdr + sizeof(tcphdr)); - return dat; -} - -std::string get_icmp6_hdr(const Icmp6Hdr &hdr) { - struct icmp6_hdr icmp6_hdr = { - .icmp6_type = (uint8_t)hdr.icmp6_type(), - .icmp6_code = (uint8_t)hdr.icmp6_code(), - .icmp6_cksum = 0, - }; - icmp6_hdr.icmp6_dataun.icmp6_un_data32[0] = hdr.icmp6_dataun(); - - std::string dat((char *)&icmp6_hdr, (char *)&icmp6_hdr + sizeof(icmp6_hdr)); - return dat; -} - -std::string get_ip6_route_hdr(const Ip6RtHdr &hdr) { - struct ip6_rthdr ip6_rthdr = { - .ip6r_nxt = (uint8_t)hdr.ip6r_nxt(), - .ip6r_len = (uint8_t)hdr.ip6r_len(), - .ip6r_type = (uint8_t)hdr.ip6r_type(), - .ip6r_segleft = (uint8_t)hdr.ip6r_segleft(), - }; - - std::string dat((char *)&ip6_rthdr, (char *)&ip6_rthdr + sizeof(ip6_rthdr)); - return dat; -} - -std::string get_ip6_route0_hdr(const Ip6Rt0Hdr &hdr) { - struct ip6_rthdr0 ip6_rthdr0 = {}; - ip6_rthdr0.ip6r0_nxt = hdr.ip6r0_nxt(); - ip6_rthdr0.ip6r0_len = hdr.ip6r0_len(); - ip6_rthdr0.ip6r0_type = hdr.ip6r0_type(); - ip6_rthdr0.ip6r0_segleft = hdr.ip6r0_segleft(); - ip6_rthdr0.ip6r0_reserved = hdr.ip6r0_reserved(); - *(uint32_t *)&ip6_rthdr0.ip6r0_slmap[0] = hdr.ip6r0_slmap(); - - int i = 0; - for (int in6addr : hdr.ip6r0_addr()) { - if (i >= 23) { - break; - } - - get_in6_addr(&ip6_rthdr0.ip6r0_addr[i], (In6Addr)in6addr); - - i++; - } - - std::string dat((char *)&ip6_rthdr0, - (char *)&ip6_rthdr0 + sizeof(ip6_rthdr0)); - return dat; -} - -std::string get_ip6_frag_hdr(const Ip6FragHdr &hdr) { - struct ip6_frag ip6_frag = { - .ip6f_nxt = (uint8_t)hdr.ip6f_nxt(), - .ip6f_reserved = (uint8_t)hdr.ip6f_reserved(), - .ip6f_offlg = (uint16_t)hdr.ip6f_offlg(), - .ip6f_ident = hdr.ip6f_ident(), - }; - - std::string dat((char *)&ip6_frag, (char *)&ip6_frag + sizeof(ip6_frag)); - return dat; -} - -std::string get_ip6_ext(const Ip6Ext &hdr) { - struct ip6_ext ip6_ext = { - .ip6e_nxt = (uint8_t)hdr.ip6e_nxt(), - .ip6e_len = (uint8_t)hdr.ip6e_len(), - }; - - std::string dat((char *)&ip6_ext, (char *)&ip6_ext + sizeof(ip6_ext)); - return dat; -} - -std::string GetNecpClient(const NecpClientId &necp_client_id) { - switch (necp_client_id) { - case CLIENT_0: { - return "0000000000000000"; - } - case CLIENT_1: { - return "1111111111111111"; - } - case CLIENT_2: { - return "2222222222222222"; - } - } - assert(false); - return ""; -} - -extern "C" { - -static FuzzedDataProvider *fdp = nullptr; - -// These are callbacks to let the C-based backend access the fuzzed input -// stream. -void get_fuzzed_bytes(void *addr, size_t bytes) { - // If we didn't initialize the fdp just clear the bytes. - if (!fdp) { - memset(addr, 0, bytes); - return; - } - memset(addr, 0, bytes); - std::vector dat = fdp->ConsumeBytes(bytes); - memcpy(addr, dat.data(), dat.size()); -} - -bool get_fuzzed_bool(void) { - // If we didn't initialize the fdp just return false. - if (!fdp) { - return false; - } - return fdp->ConsumeBool(); -} - -int get_fuzzed_int32(int low, int high) { - return fdp->ConsumeIntegralInRange(low, high); -} - -unsigned int get_fuzzed_uint32(unsigned int low, unsigned int high) { - return fdp->ConsumeIntegralInRange(low, high); -} - -unsigned int get_remaining_bytes() { return fdp->remaining_bytes(); } - -static bool ready = false; - -bool initialize_network(void); - -extern unsigned long ioctls[]; -extern int num_ioctls; -extern const unsigned long siocaifaddr_in6_64; -extern const unsigned long siocsifflags; - -// Enable this when copyout should work. -extern bool real_copyout; - -void get_in6_addrlifetime_64(struct in6_addrlifetime_64 *sai, - const In6AddrLifetime_64 &msg) { - sai->ia6t_expire = msg.ia6t_expire(); - sai->ia6t_preferred = msg.ia6t_preferred(); - sai->ia6t_vltime = msg.ia6t_vltime(); - sai->ia6t_pltime = msg.ia6t_pltime(); -} - -void get_ifr_name(void *dest, const IfrName name) { - switch (name) { - case LO0: { - memcpy(dest, "lo0", sizeof("lo0")); - break; - } - case STF0: { - memcpy(dest, "stf0", sizeof("stf0")); - break; - } - } -} - -// NECP client wrappers -// TODO(nedwill): move these to their own file -void necp_client_add(int fd, NecpClientId client_id, unsigned char *data, - size_t size) { - std::string client_id_s = GetNecpClient(client_id); - int retval = 0; - necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_ADD, - // parameters - (unsigned char *)client_id_s.data(), - client_id_s.size(), data, size, &retval); -} - -// TODO(nedwill): support flow_ifnet_stats -void necp_client_remove(int fd, NecpClientId client_id) { - std::string client_id_s = GetNecpClient(client_id); - int retval = 0; - necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_REMOVE, - (unsigned char *)client_id_s.data(), - client_id_s.size(), nullptr, 0, &retval); -} - -void necp_client_copy_parameters(int fd, NecpClientId client_id, - uint32_t copyout_size) { - std::string client_id_s = GetNecpClient(client_id); - copyout_size %= 4096; - std::unique_ptr copyout_buffer(new uint8_t[copyout_size]); - int retval = 0; - necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_COPY_PARAMETERS, - (unsigned char *)client_id_s.data(), - client_id_s.size(), copyout_buffer.get(), - copyout_size, &retval); -} - -void necp_client_agent( - int fd, NecpClientId client_id, - const ::google::protobuf::RepeatedPtrField<::NecpTlv> &necp_tlv) { - std::string client_id_s = GetNecpClient(client_id); - std::string parameters; - for (const NecpTlv &tlv : necp_tlv) { - // std::string dat((char *)&icmp6_hdr, (char *)&icmp6_hdr + - // sizeof(icmp6_hdr)); - struct necp_tlv_header header = { - .type = (uint8_t)tlv.necp_type(), - .length = (uint32_t)tlv.data().size(), - }; - std::string tlv_s((char *)&header, (char *)&header + sizeof(header)); - tlv_s += tlv.data(); - parameters += tlv_s; - } - int retval = 0; - necp_client_action_wrapper(fd, NECP_CLIENT_ACTION_AGENT, - (unsigned char *)client_id_s.data(), - client_id_s.size(), (uint8_t *)parameters.data(), - parameters.size(), &retval); -} - -void DoNecpClientAction(const NecpClientAction &necp_client_action) { - switch (necp_client_action.action_case()) { - case NecpClientAction::kAdd: { - necp_client_add(necp_client_action.necp_fd(), - necp_client_action.client_id(), - (unsigned char *)necp_client_action.add().buffer().data(), - necp_client_action.add().buffer().size()); - break; - } - case NecpClientAction::kRemove: { - necp_client_remove(necp_client_action.necp_fd(), - necp_client_action.client_id()); - break; - } - case NecpClientAction::kCopyParameters: { - necp_client_copy_parameters( - necp_client_action.necp_fd(), necp_client_action.client_id(), - necp_client_action.copy_parameters().copyout_size()); - break; - } - case NecpClientAction::kAgent: { - necp_client_agent(necp_client_action.necp_fd(), - necp_client_action.client_id(), - necp_client_action.agent().necp_tlv()); - break; - } - case NecpClientAction::ACTION_NOT_SET: { - break; - } - } -} - -void DoTcpInput(const TcpPacket &tcp_packet) { - std::string packet_s; - - size_t expected_size = - sizeof(struct ip) + sizeof(struct tcphdr) + tcp_packet.data().size(); - packet_s += get_ip_hdr(tcp_packet.ip_hdr(), expected_size); - packet_s += get_tcp_hdr(tcp_packet.tcp_hdr()); - packet_s += tcp_packet.data(); - assert(expected_size == packet_s.size()); - - if (packet_s.empty()) { - return; - } - - // TODO(nedwill): fuzz structure of mbuf itself - void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); - if (!mbuf_data) { - return; - } - - ip_input_wrapper(mbuf_data); -} - -void DoTcp6Input(const Tcp6Packet &tcp6_packet) { - std::string packet_s; - - // TODO(nedwill): support hop-by-hop and other options - size_t expected_size = sizeof(struct tcphdr) + tcp6_packet.data().size(); - packet_s += get_ip6_hdr(tcp6_packet.ip6_hdr(), expected_size); - packet_s += get_tcp_hdr(tcp6_packet.tcp_hdr()); - packet_s += tcp6_packet.data(); - - if (packet_s.empty()) { - return; - } - - void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); - if (!mbuf_data) { - return; - } - - ip6_input_wrapper(mbuf_data); -} - -void DoIp4Packet(const Ip4Packet &packet) { - size_t expected_size = sizeof(struct ip) + packet.data().size(); - std::string packet_s = get_ip_hdr(packet.ip_hdr(), expected_size); - packet_s += packet.data(); - - void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); - if (!mbuf_data) { - return; - } - - ip_input_wrapper(mbuf_data); -} - -void DoIp6Packet(const Ip6Packet &packet) { - size_t expected_size = packet.data().size(); - std::string packet_s = get_ip6_hdr(packet.ip6_hdr(), expected_size); - packet_s += packet.data(); - - void *mbuf_data = get_mbuf_data(packet_s.data(), packet_s.size(), PKTF_LOOP); - if (!mbuf_data) { - return; - } - - ip6_input_wrapper(mbuf_data); -} - -void DoIpInput(const Packet &packet) { - switch (packet.packet_case()) { - case Packet::kTcpPacket: { - DoTcpInput(packet.tcp_packet()); - break; - } - case Packet::kTcp6Packet: { - DoTcp6Input(packet.tcp6_packet()); - break; - } - case Packet::kIp4Packet: { - DoIp4Packet(packet.ip4_packet()); - break; - } - case Packet::kIp6Packet: { - DoIp6Packet(packet.ip6_packet()); - break; - } - case Packet::kRawIp4: { - void *mbuf_data = get_mbuf_data(packet.raw_ip4().data(), - packet.raw_ip4().size(), PKTF_LOOP); - if (!mbuf_data) { - return; - } - - ip_input_wrapper(mbuf_data); - break; - } - case Packet::kRawIp6: { - void *mbuf_data = get_mbuf_data(packet.raw_ip6().data(), - packet.raw_ip6().size(), PKTF_LOOP); - if (!mbuf_data) { - return; - } - - ip6_input_wrapper(mbuf_data); - break; - } - case Packet::PACKET_NOT_SET: { - break; - } - } -} - -// TODO(nedwill): make alternative build of this function that runs the -// the testcases against the real XNU syscalls, also KCOV-enabled -DEFINE_BINARY_PROTO_FUZZER(const Session &session) { - // std::string str; - // google::protobuf::TextFormat::PrintToString(session, &str); - // std::cout << str; - if (!ready) { - initialize_network(); - init_proc(); - ready = true; - } - - FuzzedDataProvider dp((const uint8_t *)session.data_provider().data(), - session.data_provider().size()); - fdp = &dp; - - // TODO(nedwill): Make these containers own the references to their - // objects so that we can use RAII to avoid leaks. - std::vector cids; - std::set open_fds; - - for (const Command &command : session.commands()) { - int retval = 0; - switch (command.command_case()) { - case Command::kSocket: { - int fd = 0; - int err = socket_wrapper(command.socket().domain(), - command.socket().so_type(), - command.socket().protocol(), &fd); - if (err == 0) { - // Make sure we're tracking fds properly. - if (open_fds.find(fd) != open_fds.end()) { - printf("Found existing fd %d\n", fd); - assert(false); - } - open_fds.insert(fd); - } - break; - } - case Command::kClose: { - open_fds.erase(command.close().fd()); - close_wrapper(command.close().fd(), nullptr); - break; - } - case Command::kSetSockOpt: { - int s = command.set_sock_opt().fd(); - int level = command.set_sock_opt().level(); - int name = command.set_sock_opt().name(); - size_t size = command.set_sock_opt().val().size(); - std::unique_ptr val(new char[size]); - memcpy(val.get(), command.set_sock_opt().val().data(), size); - setsockopt_wrapper(s, level, name, val.get(), size, nullptr); - break; - } - case Command::kGetSockOpt: { - int s = command.get_sock_opt().fd(); - int level = command.get_sock_opt().level(); - int name = command.get_sock_opt().name(); - socklen_t size = command.get_sock_opt().size(); - if (size < 0 || size > 4096) { - break; - } - std::unique_ptr val(new char[size]); - getsockopt_wrapper(s, level, name, val.get(), &size, nullptr); - break; - } - case Command::kBind: { - std::string sockaddr_s = get_sockaddr(command.bind().sockaddr()); - bind_wrapper(command.bind().fd(), (caddr_t)sockaddr_s.data(), - sockaddr_s.size(), nullptr); - break; - } - case Command::kIoctl: { - // TODO: pick these values more efficiently - // XXX: these mutate global state - uint32_t fd = command.ioctl().fd(); - uint32_t com = ioctls[command.ioctl().ioctl_idx() - 1]; - real_copyout = false; - ioctl_wrapper(fd, com, /*data=*/(caddr_t)1, nullptr); - real_copyout = true; - break; - } - case Command::kAccept: { - std::string sockaddr_s = get_sockaddr(command.accept().sockaddr()); - socklen_t size = sockaddr_s.size(); - int retval = 0; - accept_wrapper(command.accept().fd(), (caddr_t)sockaddr_s.data(), &size, - &retval); - break; - } - case Command::kIpInput: { - DoIpInput(command.ip_input()); - break; - } - case Command::kIoctlReal: { - switch (command.ioctl_real().ioctl_case()) { - case IoctlReal::kSiocaifaddrIn664: { - const In6_AliasReq_64 &req = - command.ioctl_real().siocaifaddr_in6_64(); - struct in6_aliasreq_64 alias = {}; - memcpy(alias.ifra_name, req.ifra_name().data(), - std::min(req.ifra_name().size(), sizeof(alias.ifra_name))); - get_sockaddr6(&alias.ifra_addr, req.ifra_addr()); - get_sockaddr6(&alias.ifra_dstaddr, req.ifra_dstaddr()); - get_sockaddr6(&alias.ifra_prefixmask, req.ifra_prefixmask()); - for (int flag : req.ifra_flags()) { - // make mutations that dupe a flag more useful by xoring - alias.ifra_flags ^= flag; - } - get_in6_addrlifetime_64(&alias.ifra_lifetime, req.ifra_lifetime()); - ioctl_wrapper(command.ioctl_real().fd(), siocaifaddr_in6_64, - (caddr_t)&alias, nullptr); - } - case IoctlReal::kSiocsifflags: { - struct ifreq ifreq = {}; - for (int flag : command.ioctl_real().siocsifflags().flags()) { - ifreq.ifr_flags |= flag; - } - get_ifr_name(ifreq.ifr_name, LO0); - ioctl_wrapper(command.ioctl_real().fd(), siocsifflags, - (caddr_t)&ifreq, nullptr); - break; - } - case IoctlReal::IOCTL_NOT_SET: { - break; - } - } - } - case Command::kConnectx: { - bool has_srcaddr = command.connectx().endpoints().has_sae_srcaddr(); - - std::string srcaddr_s; - if (has_srcaddr) { - srcaddr_s = - get_sockaddr(command.connectx().endpoints().sae_srcaddr()); - } - - std::string dstaddr_s = - get_sockaddr(command.connectx().endpoints().sae_dstaddr()); - - void *srcaddr = (void *)srcaddr_s.data(); - uint32_t srcsize = srcaddr_s.size(); - if (!has_srcaddr) { - srcaddr = nullptr; - assert(!srcsize); - } - - void *dstaddr = (void *)dstaddr_s.data(); - uint32_t dstsize = dstaddr_s.size(); - - // We ignore failure here since it's ok to try to send things regardless - uint32_t connectx_flags = 0; - for (const int flag : command.connectx().flags()) { - connectx_flags |= flag; - } - uint32_t cid = 0; - - struct user64_sa_endpoints endpoints = { - .sae_srcif = static_cast(command.connectx().endpoints().sae_srcif()), - .sae_srcaddr = (user64_addr_t)srcaddr, - .sae_srcaddrlen = srcsize, - .sae_dstaddr = (user64_addr_t)dstaddr, - .sae_dstaddrlen = dstsize}; - - // TODO(nedwill): is this a return value? - size_t len = 0; - // TODO(nedwill): add IOV mocking - connectx_wrapper(command.connectx().socket(), &endpoints, - command.connectx().associd(), connectx_flags, nullptr, - 0, &len, &cid, nullptr); - cids.push_back(cid); - break; - } - case Command::kConnect: { - std::string sockaddr_s = get_sockaddr(command.connect().sockaddr()); - connect_wrapper(command.connect().fd(), (caddr_t)sockaddr_s.data(), - sockaddr_s.size(), nullptr); - break; - } - case Command::kListen: { - listen_wrapper(command.listen().socket(), command.listen().backlog(), - nullptr); - break; - } - case Command::kDisconnectx: { - uint32_t cid = 0; - if (!cids.empty()) { - cid = cids[command.disconnectx().cid() % cids.size()]; - } else { - cid = command.disconnectx().cid(); - } - disconnectx_wrapper(command.disconnectx().fd(), - command.disconnectx().associd(), cid, nullptr); - break; - } - case Command::kClearAll: { - // TODO(nedwill): finer-grained calling of clear_all functions here - clear_all(); - break; - } - case Command::kNecpMatchPolicy: { - std::unique_ptr parameters_u8( - new uint8_t[command.necp_match_policy().parameters().size()]); - memcpy(parameters_u8.get(), - command.necp_match_policy().parameters().data(), - command.necp_match_policy().parameters().size()); - // necp_match_policy_wrapper( - // parameters_u8.get(), - // command.necp_match_policy().parameters().size(), - // /*returned_result*/ nullptr, nullptr); - break; - } - case Command::kNecpOpen: { - // int flags = 0; - // for (int flag : command.necp_open().flags()) { - // flags |= flag; - // } - // int fd = 0; - // int err = necp_open_wrapper(flags, &fd); - // if (err == 0) { - // if (open_fds.find(fd) != open_fds.end()) { - // printf("fd %d already in open sockets\n", fd); - // assert(false); - // } - // open_fds.insert(fd); - // } - break; - } - case Command::kNecpClientAction: { - // DoNecpClientAction(command.necp_client_action()); - break; - } - case Command::kNecpSessionOpen: { - // int flags = 0; - // int fd = 0; - // int err = necp_session_open_wrapper(flags, &fd); - // if (err == 0) { - // if (open_fds.find(fd) != open_fds.end()) { - // printf("fd %d already in open sockets\n", fd); - // assert(false); - // } - // open_fds.insert(fd); - // } - break; - } - case Command::kNecpSessionAction: { - size_t out_buffer_size = - command.necp_session_action().out_buffer_size() % 4096; - std::unique_ptr out_buffer(new uint8_t[out_buffer_size]); - // necp_session_action_wrapper( - // command.necp_session_action().necp_fd(), - // command.necp_session_action().action(), - // // TODO(nedwill): fix const cast - // (uint8_t *)command.necp_session_action().in_buffer().data(), - // command.necp_session_action().in_buffer().size(), out_buffer.get(), - // out_buffer_size, &retval); - break; - } - case Command::kAcceptNocancel: { - std::string sockaddr_s = get_sockaddr(command.accept_nocancel().name()); - socklen_t size = sockaddr_s.size(); - accept_nocancel_wrapper(command.accept_nocancel().s(), - (caddr_t)sockaddr_s.data(), &size, &retval); - break; - } - case Command::kConnectNocancel: { - std::string sockaddr_s = - get_sockaddr(command.connect_nocancel().name()); - socklen_t size = sockaddr_s.size(); - connect_nocancel_wrapper(command.connect_nocancel().s(), - (caddr_t)sockaddr_s.data(), size, &retval); - break; - } - case Command::kGetpeername: { - std::string sockaddr_s = get_sockaddr(command.getpeername().asa()); - socklen_t size = sockaddr_s.size(); - getpeername_wrapper(command.getpeername().fdes(), - (caddr_t)sockaddr_s.data(), &size, &retval); - break; - } - case Command::kGetsockname: { - std::string sockaddr_s = get_sockaddr(command.getsockname().asa()); - socklen_t size = sockaddr_s.size(); - getsockname_wrapper(command.getsockname().fdes(), - (caddr_t)sockaddr_s.data(), &size, &retval); - break; - } - case Command::kPeeloff: { - peeloff_wrapper(command.peeloff().s(), command.peeloff().aid(), - &retval); - break; - } - case Command::kRecvfrom: { - std::string sockaddr_s = get_sockaddr(command.recvfrom().from()); - int size = sockaddr_s.size(); - // TODO(nedwill): just specify the size of the buffer, don't store bytes - // in the proto message just for them to be overwritten - recvfrom_wrapper( - command.recvfrom().s(), (caddr_t)command.recvfrom().buf().data(), - command.recvfrom().buf().size(), command.recvfrom().flags(), - (struct sockaddr *)sockaddr_s.data(), &size, &retval); - break; - } - case Command::kRecvfromNocancel: { - std::string sockaddr_s = - get_sockaddr(command.recvfrom_nocancel().from()); - int size = sockaddr_s.size(); - recvfrom_nocancel_wrapper( - command.recvfrom_nocancel().s(), - (caddr_t)command.recvfrom_nocancel().buf().data(), - command.recvfrom_nocancel().buf().size(), - command.recvfrom_nocancel().flags(), - (struct sockaddr *)sockaddr_s.data(), &size, &retval); - break; - } - case Command::kRecvmsg: { - // TODO(nedwill): fuzz this msg field - user64_msghdr msg = {}; - recvmsg_wrapper(command.recvmsg().s(), (struct msghdr *)&msg, - command.recvmsg().flags(), &retval); - break; - } - case Command::kSendto: { - std::string sockaddr_s = get_sockaddr(command.sendto().to()); - socklen_t size = sockaddr_s.size(); - sendto_wrapper(command.sendto().s(), - (caddr_t)command.sendto().buf().data(), - command.sendto().buf().size(), command.sendto().flags(), - (caddr_t)sockaddr_s.data(), size, &retval); - break; - } - case Command::kSocketpair: { - int rsv[2] = {}; - int ret = socketpair_wrapper( - command.socketpair().domain(), command.socketpair().type(), - command.socketpair().protocol(), rsv, &retval); - if (!ret) { - if (open_fds.find(rsv[0]) != open_fds.end()) { - assert(false); - } - open_fds.insert(rsv[0]); - if (open_fds.find(rsv[1]) != open_fds.end()) { - assert(false); - } - open_fds.insert(rsv[1]); - } - break; - } - case Command::kPipe: { - int rsv[2] = {}; - int ret = pipe_wrapper(rsv); - if (!ret) { - if (open_fds.find(rsv[0]) != open_fds.end()) { - assert(false); - } - open_fds.insert(rsv[0]); - if (open_fds.find(rsv[1]) != open_fds.end()) { - assert(false); - } - open_fds.insert(rsv[1]); - } - break; - } - case Command::kShutdown: { - shutdown_wrapper(command.shutdown().s(), command.shutdown().how(), - &retval); - break; - } - case Command::COMMAND_NOT_SET: { - break; - } - } - } - - for (int fd : open_fds) { - int err = close_wrapper(fd, nullptr); - assert(err != EBADF); - } - - // TODO(nedwill): split up these worker threads and run them as - // part of main loop - clear_all(); -} -} diff --git a/fuzz/net_fuzzer.options b/fuzz/net_fuzzer.options deleted file mode 100644 index add6b76..0000000 --- a/fuzz/net_fuzzer.options +++ /dev/null @@ -1,4 +0,0 @@ -[libfuzzer] -fork = 1 -max_len = 4096 -use_value_profile = 1 diff --git a/fuzz/net_fuzzer.proto b/fuzz/net_fuzzer.proto deleted file mode 100644 index 7c4a031..0000000 --- a/fuzz/net_fuzzer.proto +++ /dev/null @@ -1,1676 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - -syntax = "proto2"; - -message Session { - repeated Command commands = 1; - required bytes data_provider = 2; -} - -message Connect { - required SockAddr sockaddr = 1; - required FileDescriptor fd = 2; -} - -message ConnectX { - required FileDescriptor socket = 1; - required Endpoints endpoints = 2; - required SaeAssocID associd = 3; - repeated ConnectXFlag flags = 4; - // user_addr_t iov; - // unsigned int iovcnt; - // TODO(nedwill): support IOV - // repeated IOV connectx_iovs = 5; -} - -message DisconnectX { - required SaeAssocID associd = 1; - required int32 cid = 2; - required FileDescriptor fd = 3; -} - -enum SaeAssocID { - ASSOCID_CASE_0 = 0; - ASSOCID_CASE_1 = 1; - ASSOCID_CASE_2 = 2; - ASSOCID_CASE_3 = 3; - // Note: negatives are inefficient in enum, perhaps we should - // just subtract 1 from the enum value as a hack. - ASSOCID_CASE_END = -1; -} - -message IOV { - required bytes iov_data = 1; -} - -message Endpoints { - required IfIdx sae_srcif = 1; - optional SockAddr sae_srcaddr = 2; - required SockAddr sae_dstaddr = 3; -} - -message SockAddr { - oneof sockaddr { - SockAddrGeneric sockaddr_generic = 1; - // Legit IPv4 sockaddr - SockAddr4 sockaddr4 = 2; - // Legit IPv6 sockaddr - SockAddr6 sockaddr6 = 3; - // TODO(nedwill): Legit UNIX socket - // SockAddrUn sockaddrun = 3; - - // TODO(nedwill): sockaddr_ctl, sockaddr_* - - // Fuzz raw parsing of sockaddrs - // bytes raw_bytes = 1000; - } -} - -message SockAddrGeneric { - required Domain sa_family = 1; - required bytes sa_data = 2; -} - -message SockAddr4 { - required Domain sin_family = 1; - required Port sin_port = 2; - required InAddr sin_addr = 3; -} - -message SockAddr6 { - required Domain family = 1; - required Port port = 2; - required FlowInfo flow_info = 3; - required In6Addr sin6_addr = 4; - required ScopeId sin6_scope_id = 5; -} - -enum In6Addr { - IN6_ADDR_ANY = 0; - IN6_ADDR_LOOPBACK = 1; - IN6_ADDR_REAL = 2; - IN6_ADDR_SELF = 3; - IN6_ADDR_LINK_LOCAL = 4; - IN6_ADDR_UNSPECIFIED = 5; - IN6_ADDR_V4COMPAT = 7; - IN6_ADDR_V4MAPPED = 8; - IN6_ADDR_6TO4 = 9; - IN6_ADDR_LINKLOCAL = 10; - IN6_ADDR_SITELOCAL = 11; - IN6_ADDR_MULTICAST = 12; - IN6_ADDR_UNIQUE_LOCAL = 13; - IN6_ADDR_MC_NODELOCAL = 14; - IN6_ADDR_MC_INTFACELOCAL = 15; - IN6_ADDR_MC_LINKLOCAL = 16; - IN6_ADDR_MC_SITELOCAL = 17; - IN6_ADDR_MC_ORGLOCAL = 18; - IN6_ADDR_MC_GLOBAL = 19; - IN6_ADDR_LOCAL_ADDRESS = 20; - MAYBE_LOCALHOST = 16777216; -} - -// These can be used as-is for 32-bit ints. -enum InAddr { - IN4_ADDR_0 = 0; - IN4_ADDR_1 = 1; - IN4_ADDR_2 = 2; - // 24.130.58.208 - IN4_ADDR_4 = 411187920; - // 127.0.0.1 - IN4_ADDR_5 = 2130706433; - // 192.168.86.88 - IN4_ADDR_6 = -1062709672; - IN4_ADDR_BROADCAST = -1; -} - -enum FlowInfo { - FLOW_INFO_0 = 0; - FLOW_INFO_1 = 1; - FLOW_INFO_2 = 2; - FLOW_INFO_3 = 3; -} - -enum ScopeId { - SCOPE_ID_0 = 0; - SCOPE_ID_1 = 1; - SCOPE_ID_2 = 2; - SCOPE_ID_3 = 3; - SCOPE_ID_50 = 50; -} - -enum Port { - PORT_0 = 0; - PORT_1 = 1; - PORT_2 = 2; - PORT_5555 = 5555; - PORT_65000 = 65000; -} - -// Interface index obtained from if_nametoindex -// TODO(nedwill): do something more legit here -enum IfIdx { - IFIDX_CASE_0 = 0; - IFIDX_CASE_1 = 1; - IFIDX_CASE_2 = 2; - IFIDX_CASE_3 = 3; -} - -enum ConnectXFlag { - CONNECT_RESUME_ON_READ_WRITE = 1; - CONNECT_DATA_IDEMPOTENT = 2; - CONNECT_DATA_AUTHENTICATED = 4; -} - -message Command { - oneof command { - Packet ip_input = 1; - // Key key = 2; - // Old ioctl style using lookup table - Ioctl ioctl = 3; - SetSocketOpt set_sock_opt = 4; - // New style using protobuf - IoctlReal ioctl_real = 5; - ConnectX connectx = 6; - Connect connect = 7; - Listen listen = 8; - DisconnectX disconnectx = 9; - GetSocketOpt get_sock_opt = 10; - Bind bind = 11; - Accept accept = 12; - Socket socket = 13; - Close close = 14; - ClearAll clear_all = 15; - NecpMatchPolicy necp_match_policy = 16; - NecpOpen necp_open = 17; - NecpClientAction necp_client_action = 18; - NecpSessionOpen necp_session_open = 19; - NecpSessionAction necp_session_action = 20; - AcceptNocancel accept_nocancel = 21; - ConnectNocancel connect_nocancel = 22; - Getpeername getpeername = 23; - Getsockname getsockname = 24; - Peeloff peeloff = 25; - Recvfrom recvfrom = 26; - RecvfromNocancel recvfrom_nocancel = 27; - Recvmsg recvmsg = 28; - // RecvmsgNocancel recvmsg_nocancel = 29; - // RecvmsgX recvmsg_x = 30; - // Sendmsg sendmsg = 31; - // SendmsgNocancel sendmsg_nocancel = 32; - // SendmsgX sendmsg_x = 33; - Sendto sendto = 34; - // SendtoNocancel sendto_nocancel = 35; - // Shutdown shutdown = 36; - // SocketDelegate socket_delegate = 37; - Socketpair socketpair = 38; - Pipe pipe = 39; - Shutdown shutdown = 40; - } -} - -message AcceptNocancel { - optional FileDescriptor s = 1; - optional SockAddr name = 2; -} - -message ConnectNocancel { - optional FileDescriptor s = 1; - optional SockAddr name = 2; -} - -message Getpeername { - optional FileDescriptor fdes = 1; - optional SockAddr asa = 2; -} - -message Getsockname { - optional FileDescriptor fdes = 1; - optional SockAddr asa = 2; -} - -message Peeloff { - optional FileDescriptor s = 1; - optional SaeAssocID aid = 2; -} - -message Pipe {} - -message Shutdown { - optional FileDescriptor s = 1; - optional ShutdownHow how = 2; -} - -enum ShutdownHow { - SHUT_RD = 0; - SHUT_WR = 1; - SHUT_RDWR = 2; -} - -message Recvfrom { - optional FileDescriptor s = 1; - optional bytes buf = 2; - optional int32 flags = 3; - optional SockAddr from = 4; -} - -message RecvfromNocancel { - optional FileDescriptor s = 1; - optional bytes buf = 2; - optional int32 flags = 3; - optional SockAddr from = 4; -} - -message Recvmsg { - optional FileDescriptor s = 1; - // optional StructMsghdr msg = 2; - optional uint32 flags = 2; -} - -// message RecvmsgNocancel { -// optional int32 s = 1; -// optional StructMsghdr msg = 2; -// optional RecvmsgNocancelFlags flags = 3; -// } - -// message RecvmsgX { -// optional int32 s = 1; -// optional StructMsghdrx msgp = 2; -// optional uint32 cnt = 3; -// optional RecvmsgXFlags flags = 4; -// } - -message Sendmsg { - optional FileDescriptor s = 1; - optional bytes msg = 2; - optional uint32 flags = 3; -} - -// message SendmsgNocancel { -// optional int32 s = 1; -// optional bytes msg = 2; -// optional SendmsgNocancelFlags flags = 3; -// } - -// message SendmsgX { -// optional int32 s = 1; -// optional StructMsghdrx msgp = 2; -// optional uint32 cnt = 3; -// optional SendmsgXFlags flags = 4; -// } - -message Sendto { - optional FileDescriptor s = 1; - optional bytes buf = 2; - optional int32 flags = 3; - optional SockAddr to = 4; -} - -// message SendtoNocancel { -// optional int32 s = 1; -// optional bytes buf = 2; -// optional uint64 len = 3; -// optional int32 flags = 4; -// optional bytes to = 5; -// } - -// message Shutdown { -// optional int32 s = 1; -// optional int32 how = 2; -// } - -// message SocketDelegate { -// optional int32 domain = 1; -// optional int32 type = 2; -// optional int32 protocol = 3; -// optional Pid epid = 4; -// } - -message Socketpair { - optional Domain domain = 1; - optional SoType type = 2; - optional Protocol protocol = 3; -} - -message NecpSessionOpen { - // Flags argument is unused - -} - -message NecpSessionAction { - optional FileDescriptor necp_fd = 1; - optional NecpSessionActionNumber action = 2; - optional bytes in_buffer = 3; - optional uint32 out_buffer_size = 4; -} - -message NecpMatchPolicy { - optional bytes parameters = 1; -} - -message NecpOpen { - repeated NecpOpenFlag flags = 1; -} - -enum NecpOpenFlag { - NECP_OPEN_FLAG_OBSERVER = 1; // Observers can query clients they don't own - NECP_OPEN_FLAG_BACKGROUND = 2; // Mark this fd as backgrounded - NECP_OPEN_FLAG_PUSH_OBSERVER = 4; // When used with the OBSERVER flag, allows updates to be pushed. Adding clients is not allowed in this mode. -} - -// TODO(nedwill): support null client -enum NecpClientId { - CLIENT_0 = 0; - CLIENT_1 = 1; - CLIENT_2 = 2; -} - -message NecpClientAction { - optional FileDescriptor necp_fd = 1; - optional NecpClientId client_id = 3; - // TODO(nedwill): remove these fields - // optional NecpClientActionNumber action = 2; - // optional bytes buffer = 4; - - oneof action { - NecpClientActionAdd add = 5; - NecpClientActionRemove remove = 6; - NecpClientActionCopyParameters copy_parameters = 7; - // TODO(nedwill): implement remaining necp client commands - // NECP_CLIENT_ACTION_COPY_RESULT = 8; // Copy client result. Input: client_id; Output: result in buffer - // NECP_CLIENT_ACTION_COPY_LIST = 9; // Copy all client IDs. Output: struct necp_client_list in buffer - // TODO(nedwill): this isn't actually used? - // NecpClientActionRequestNexusInstance request_nexus_instance = 10; - NecpClientActionAgent agent = 11; - // NECP_CLIENT_ACTION_COPY_AGENT = 12; // Copy agent content. Input: agent UUID; Output: struct netagent - // NECP_CLIENT_ACTION_COPY_INTERFACE = 13; // Copy interface details. Input: ifindex cast to UUID; Output: struct necp_interface_details - // NECP_CLIENT_ACTION_SET_STATISTICS = 14; // Deprecated - // NECP_CLIENT_ACTION_COPY_ROUTE_STATISTICS = 15; // Get route statistics. Input: client_id; Output: struct necp_stat_counts - // NECP_CLIENT_ACTION_AGENT_USE = 16; // Return the use count and increment the use count. Input/Output: struct necp_agent_use_parameters - // NECP_CLIENT_ACTION_MAP_SYSCTLS = 17; // Get the read-only sysctls memory location. Output: mach_vm_address_t - // NECP_CLIENT_ACTION_UPDATE_CACHE = 18; // Update heuristics and cache - // NECP_CLIENT_ACTION_COPY_CLIENT_UPDATE = 19; // Fetch an updated client for push-mode observer. Output: Client id, struct necp_client_observer_update in buffer - // NECP_CLIENT_ACTION_COPY_UPDATED_RESULT = 20; // Copy client result only if changed. Input: client_id; Output: result in buffer - // TODO(nedwill): these aren't implemented? - // NECP_CLIENT_ACTION_ADD_FLOW = 21; // Add a flow. Input: client_id; Output: struct necp_client_add_flow - // NECP_CLIENT_ACTION_REMOVE_FLOW = 22; // Remove a flow. Input: flow_id, optional struct ifnet_stats_per_flow - - } -} - -message NecpClientActionAdd { - optional bytes buffer = 1; -} - -message NecpClientActionRemove {} - -message NecpClientActionCopyParameters { - optional uint32 copyout_size = 1; -} - -message NecpClientActionAgent { - repeated NecpTlv necp_tlv = 1; -} - -message NecpTlv { - // TODO(nedwill): make this an enum - optional uint32 necp_type = 1; - optional bytes data = 2; -} - -enum NecpClientActionNumber { - NECP_CLIENT_ACTION_ADD = 1; // Register a new client. Input: parameters in buffer; Output: client_id - NECP_CLIENT_ACTION_REMOVE = 2; // Unregister a client. Input: client_id, optional struct ifnet_stats_per_flow - NECP_CLIENT_ACTION_COPY_PARAMETERS = 3; // Copy client parameters. Input: client_id; Output: parameters in buffer - NECP_CLIENT_ACTION_COPY_RESULT = 4; // Copy client result. Input: client_id; Output: result in buffer - NECP_CLIENT_ACTION_COPY_LIST = 5; // Copy all client IDs. Output: struct necp_client_list in buffer - NECP_CLIENT_ACTION_REQUEST_NEXUS_INSTANCE = 6; // Request a nexus instance from a nexus provider, optional struct necp_stats_bufreq - NECP_CLIENT_ACTION_AGENT = 7; // Interact with agent. Input: client_id, agent parameters - NECP_CLIENT_ACTION_COPY_AGENT = 8; // Copy agent content. Input: agent UUID; Output: struct netagent - NECP_CLIENT_ACTION_COPY_INTERFACE = 9; // Copy interface details. Input: ifindex cast to UUID; Output: struct necp_interface_details - NECP_CLIENT_ACTION_SET_STATISTICS = 10; // Deprecated - NECP_CLIENT_ACTION_COPY_ROUTE_STATISTICS = 11; // Get route statistics. Input: client_id; Output: struct necp_stat_counts - NECP_CLIENT_ACTION_AGENT_USE = 12; // Return the use count and increment the use count. Input/Output: struct necp_agent_use_parameters - NECP_CLIENT_ACTION_MAP_SYSCTLS = 13; // Get the read-only sysctls memory location. Output: mach_vm_address_t - NECP_CLIENT_ACTION_UPDATE_CACHE = 14; // Update heuristics and cache - NECP_CLIENT_ACTION_COPY_CLIENT_UPDATE = 15; // Fetch an updated client for push-mode observer. Output: Client id, struct necp_client_observer_update in buffer - NECP_CLIENT_ACTION_COPY_UPDATED_RESULT = 16; // Copy client result only if changed. Input: client_id; Output: result in buffer - NECP_CLIENT_ACTION_ADD_FLOW = 17; // Add a flow. Input: client_id; Output: struct necp_client_add_flow - NECP_CLIENT_ACTION_REMOVE_FLOW = 18; // Remove a flow. Input: flow_id, optional struct ifnet_stats_per_flow -} - -enum NecpSessionActionNumber { - NECP_SESSION_ACTION_POLICY_ADD = 1; // In: Policy TLVs Out: necp_policy_id - NECP_SESSION_ACTION_POLICY_GET = 2; // In: necp_policy_id Out: Policy TLVs - NECP_SESSION_ACTION_POLICY_DELETE = 3; // In: necp_policy_id Out: None - NECP_SESSION_ACTION_POLICY_APPLY_ALL = 4; // In: None Out: None - NECP_SESSION_ACTION_POLICY_LIST_ALL = 5; // In: None Out: TLVs of IDs - NECP_SESSION_ACTION_POLICY_DELETE_ALL = 6; // In: None Out: None - NECP_SESSION_ACTION_SET_SESSION_PRIORITY = 7; // In: necp_session_priority Out: None - NECP_SESSION_ACTION_LOCK_SESSION_TO_PROC = 8; // In: None Out: None - NECP_SESSION_ACTION_REGISTER_SERVICE = 9; // In: uuid_t Out: None - NECP_SESSION_ACTION_UNREGISTER_SERVICE = 10; // In: uuid_t Out: None - NECP_SESSION_ACTION_POLICY_DUMP_ALL = 11; // In: None Out: uint32_t bytes length, then Policy TLVs -} - -message ClearAll {} - -message Close { - required FileDescriptor fd = 1; -} - -message Accept { - required SockAddr sockaddr = 1; - optional FileDescriptor fd = 2; -} - -message Bind { - required SockAddr sockaddr = 1; - optional FileDescriptor fd = 2; -} - -message Listen { - required FileDescriptor socket = 1; - optional int32 backlog = 2; -} - -message IoctlReal { - oneof ioctl { - In6_AliasReq_64 siocaifaddr_in6_64 = 1; - IfReqFlags siocsifflags = 3; - } - optional FileDescriptor fd = 2; -} - -enum IfrName { - LO0 = 0; - STF0 = 1; -} - -enum IfruFlag { - IFF_UP = 1; - IFF_BROADCAST = 2; - IFF_DEBUG = 4; - IFF_LOOPBACK = 8; - IFF_POINTOPOINT = 16; - IFF_NOTRAILERS = 32; - IFF_RUNNING = 64; - IFF_NOARP = 128; - IFF_PROMISC = 256; - IFF_ALLMULTI = 512; - IFF_OACTIVE = 1024; - IFF_SIMPLEX = 2048; - IFF_LINK0 = 4096; - IFF_LINK1 = 8192; - IFF_LINK2 = 16384; - IFF_MULTICAST = 32768; -} - -// ifreq.ifr_flags -message IfReqFlags { - required IfrName ifr_name = 1; - // #define ifr_flags ifr_ifru.ifru_flags /* flags */ - repeated IfruFlag flags = 2; -} - -message In6_AliasReq_64 { - required bytes ifra_name = 1; - required SockAddr6 ifra_addr = 2; - required SockAddr6 ifra_dstaddr = 3; - required SockAddr6 ifra_prefixmask = 4; - repeated IfraFlag ifra_flags = 5; - required In6AddrLifetime_64 ifra_lifetime = 6; -} - -enum IfraFlag { - IN6_IFF_ANYCAST = 1; // anycast address - IN6_IFF_TENTATIVE = 2; // tentative address - IN6_IFF_DUPLICATED = 4; // DAD detected duplicate - IN6_IFF_DETACHED = 8; // may be detached from the link - IN6_IFF_DEPRECATED = 16; // deprecated address - IN6_IFF_NODAD = 32; - IN6_IFF_AUTOCONF = 64; // autoconfigurable address. - IN6_IFF_TEMPORARY = 128; // temporary (anonymous) address. - IN6_IFF_DYNAMIC = 256; // assigned by DHCPv6 service - IN6_IFF_OPTIMISTIC = 512; // optimistic DAD, i.e. RFC 4429 - IN6_IFF_SECURED = 1024; // cryptographically generated - IN6_IFF_SWIFTDAD = 2048; // DAD with no delay - IN6_IFF_CLAT46 = 4096; // Address reserved for CLAT46 - IN6_IFF_NOPFX = 32768; // Depreciated. Don't use. -} - -message In6AddrLifetime_64 { - required uint64 ia6t_expire = 1; - required uint64 ia6t_preferred = 2; - required uint32 ia6t_vltime = 3; - required uint32 ia6t_pltime = 4; -} - -message Packet { - oneof packet { - TcpPacket tcp_packet = 1; - Tcp6Packet tcp6_packet = 2; - Ip4Packet ip4_packet = 3; - Ip6Packet ip6_packet = 4; - // TODO(nedwill): more packet types - // Stf4Packet, - // Udp6Packet, - // Rip6Packet, - // Icmp6Packet, - // Dest6Packet, - // Route6Packet, - // Frag6Packet, - // Ah6Packet, - // Esp6Packet, - // Ipcomp6Packet, - // Encap6Packet, - // Gif4Packet, - // Gif6Packet, - // Udp4Packet, - // Rip4Packet, - // Icmp4Packet, - // Igmp4Packet, - // Gre4Packet, - // Ah4Packet, - // Esp4Packet, - // Ipcomp4Packet, - // Encap4Packet, - // Div4Packet, - - // TODO(nedwill): enable this once other types are finished - bytes raw_ip4 = 1000; - bytes raw_ip6 = 1001; - } - // TODO(nedwill): specify metadata here for layout of mbuf - -} - -message TcpPacket { - required IpHdr ip_hdr = 1; - required TcpHdr tcp_hdr = 2; - optional bytes data = 3; -} - -message Tcp6Packet { - required Ip6Hdr ip6_hdr = 1; - required TcpHdr tcp_hdr = 2; - optional bytes data = 3; -} - -message Ip4Packet { - optional IpHdr ip_hdr = 1; - optional bytes data = 2; -} - -message Ip6Packet { - optional Ip6Hdr ip6_hdr = 1; - optional bytes data = 2; -} - -message Ip6Ext { - required Protocol ip6e_nxt = 1; - required uint32 ip6e_len = 2; -} - -message Ip6RtHdr { - required Protocol ip6r_nxt = 1; - required uint32 ip6r_len = 2; - required Ip6RtType ip6r_type = 3; - required uint32 ip6r_segleft = 4; -} - -// Currently has only one type supported in XNU -enum Ip6RtType { - IPV6_RTHDR_TYPE_0 = 0; -} - -message Ip6Rt0Hdr { - required Protocol ip6r0_nxt = 1; - required uint32 ip6r0_len = 2; - required uint32 ip6r0_type = 3; - required uint32 ip6r0_segleft = 4; - required uint32 ip6r0_reserved = 5; - required uint32 ip6r0_slmap = 6; - repeated In6Addr ip6r0_addr = 7; -} - -message Ip6FragHdr { - required Protocol ip6f_nxt = 1; - required uint32 ip6f_reserved = 2; - required uint32 ip6f_offlg = 3; - required uint32 ip6f_ident = 4; -} - -message Icmp6Hdr { - optional Icmp6Type icmp6_type = 1; - optional Icmp6Code icmp6_code = 2; - // optional uint32 icmp6_cksum = 3; - optional uint32 icmp6_dataun = 4; -} - -enum Icmp6Type { - ICMP6_DST_UNREACH = 1; // dest unreachable, codes: - ICMP6_PACKET_TOO_BIG = 2; // packet too big - ICMP6_TIME_EXCEEDED = 3; // time exceeded, code: - ICMP6_PARAM_PROB = 4; // ip6 header bad - ICMP6_ECHO_REQUEST = 128; // echo service - ICMP6_ECHO_REPLY = 129; // echo reply - MLD_LISTENER_QUERY = 130; // multicast listener query - MLD_LISTENER_REPORT = 131; // multicast listener report - MLD_LISTENER_DONE = 132; // multicast listener done - ND_ROUTER_SOLICIT = 133; // router solicitation - ND_ROUTER_ADVERT = 134; // router advertisement - ND_NEIGHBOR_SOLICIT = 135; // neighbor solicitation - ND_NEIGHBOR_ADVERT = 136; // neighbor advertisement - ND_REDIRECT = 137; // redirect - ICMP6_ROUTER_RENUMBERING = 138; // router renumbering - ICMP6_WRUREQUEST = 139; // who are you request - ICMP6_WRUREPLY = 140; // who are you reply - MLDV2_LISTENER_REPORT = 143; // RFC3810 listener report - MLD_MTRACE_RESP = 200; // mtrace resp (to sender) - MLD_MTRACE = 201; // mtrace messages -} - -enum Icmp6Code { - ICMP6_DST_UNREACH_NOROUTE = 0; // no route to destination - ICMP6_DST_UNREACH_ADMIN = 1; // administratively prohibited - ICMP6_DST_UNREACH_NOTNEIGHBOR = 2; // not a neighbor(obsolete) - ICMP6_DST_UNREACH_ADDR = 3; // address unreachable - ICMP6_DST_UNREACH_NOPORT = 4; // port unreachable - ICMP6_INFOMSG_MASK = 128; // all informational messages - ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET = 255; // rr seq num reset -} - -enum TcpSeq { - SEQ_1 = 1; - SEQ_2 = 2; - SEQ_3 = 3; - SEQ_4 = 4; - SEQ_5 = 5; - SEQ_6 = 6; - SEQ_7 = 7; - SEQ_8 = 8; -} - -message TcpHdr { - required Port th_sport = 1; - required Port th_dport = 2; - required TcpSeq th_seq = 3; - required TcpSeq th_ack = 4; - required uint32 th_off = 5; - repeated TcpFlag th_flags = 6; - required uint32 th_win = 7; - required uint32 th_sum = 8; - required uint32 th_urp = 9; - // Ned's extensions - required bool is_pure_syn = 10; - required bool is_pure_ack = 11; -} - -enum TcpFlag { - TH_FIN = 1; - TH_SYN = 2; - TH_RST = 4; - TH_PUSH = 8; - TH_ACK = 16; - TH_URG = 32; - TH_ECE = 64; - TH_CWR = 128; -} - -enum IpVersion { - IPV4 = 4; - IPV6 = 6; -} - -// TODO(nedwill): ip options -message IpHdr { - required uint32 ip_hl = 1; - required IpVersion ip_v = 2; - required uint32 ip_tos = 3; - required uint32 ip_len = 4; - required uint32 ip_id = 5; - required uint32 ip_off = 6; - required uint32 ip_ttl = 7; - required Protocol ip_p = 8; - required InAddr ip_src = 9; - required InAddr ip_dst = 10; -} - -message Ip6Hdr { - required Ip6Hdrctl ip6_hdrctl = 1; - required In6Addr ip6_src = 2; - required In6Addr ip6_dst = 3; -} - -message Ip6Hdrctl { - required uint32 ip6_un1_flow = 1; - required uint32 ip6_un1_plen = 2; - required Protocol ip6_un1_nxt = 3; - required uint32 ip6_un1_hlim = 4; -} - -message Key { - required bytes mbuf_data = 1; -} - -message Ioctl { - required IoctlIdx ioctl_idx = 1; - required FileDescriptor fd = 2; -} - -// TODO(nedwill): review all commented (potentially nondeterministic) ioctls -// to ensure determinism on each loop with manual cleanup -enum IoctlIdx { - SIOCSHIWAT = 1; - SIOCGHIWAT = 2; - SIOCSLOWAT = 3; - SIOCGLOWAT = 4; - SIOCATMARK = 5; - SIOCSPGRP = 6; - SIOCGPGRP = 7; - SIOCSIFADDR = 8; - OSIOCGIFADDR = 9; - SIOCSIFDSTADDR = 10; - OSIOCGIFDSTADDR = 11; - SIOCSIFFLAGS = 12; - SIOCGIFFLAGS = 13; - OSIOCGIFBRDADDR = 14; - SIOCSIFBRDADDR = 15; - OSIOCGIFCONF = 16; - OSIOCGIFCONF32 = 17; - OSIOCGIFCONF64 = 18; - OSIOCGIFNETMASK = 19; - SIOCSIFNETMASK = 20; - SIOCGIFMETRIC = 21; - SIOCSIFMETRIC = 22; - SIOCDIFADDR = 23; - SIOCAIFADDR = 24; - SIOCGIFADDR = 25; - SIOCGIFDSTADDR = 26; - SIOCGIFBRDADDR = 27; - SIOCGIFCONF = 28; - SIOCGIFCONF32 = 29; - SIOCGIFCONF64 = 30; - SIOCGIFNETMASK = 31; - SIOCADDMULTI = 32; - SIOCDELMULTI = 33; - SIOCGIFMTU = 34; - SIOCSIFMTU = 35; - SIOCGIFPHYS = 36; - SIOCSIFPHYS = 37; - SIOCSIFMEDIA = 38; - SIOCGIFMEDIA = 39; - SIOCGIFMEDIA32 = 40; - SIOCGIFMEDIA64 = 41; - SIOCSIFGENERIC = 42; - SIOCGIFGENERIC = 43; - SIOCSIFLLADDR = 44; - SIOCGIFSTATUS = 45; - SIOCSIFPHYADDR = 46; - SIOCGIFPSRCADDR = 47; - SIOCGIFPDSTADDR = 48; - SIOCDIFPHYADDR = 49; - SIOCGIFDEVMTU = 50; - SIOCSIFALTMTU = 51; - SIOCPROTOATTACH = 52; - SIOCPROTODETACH = 53; - SIOCIFCREATE = 54; - SIOCIFDESTROY = 55; - SIOCSIFVLAN = 56; - SIOCGIFVLAN = 57; - SIOCSETVLAN = 58; - SIOCGETVLAN = 59; - SIOCSIFDEVMTU = 60; - SIOCIFGCLONERS = 61; - SIOCIFGCLONERS32 = 62; - SIOCIFGCLONERS64 = 63; - SIOCGIFASYNCMAP = 64; - SIOCSIFASYNCMAP = 65; - SIOCSIFKPI = 66; - SIOCGIFKPI = 67; - SIOCGIFWAKEFLAGS = 68; - SIOCGIFGETRTREFCNT = 69; - SIOCGIFLINKQUALITYMETRIC = 70; - SIOCSETROUTERMODE = 71; - SIOCGIFEFLAGS = 72; - SIOCSIFDESC = 73; - SIOCGIFDESC = 74; - SIOCSIFLINKPARAMS = 75; - SIOCGIFLINKPARAMS = 76; - SIOCGIFQUEUESTATS = 77; - SIOCSIFTHROTTLE = 78; - SIOCGIFTHROTTLE = 79; - SIOCGASSOCIDS = 80; - SIOCGCONNIDS = 81; - SIOCGCONNINFO = 82; - SIOCGASSOCIDS32 = 83; - SIOCGASSOCIDS64 = 84; - SIOCGCONNIDS32 = 85; - SIOCGCONNIDS64 = 86; - SIOCGCONNINFO32 = 87; - SIOCGCONNINFO64 = 88; - SIOCSCONNORDER = 89; - SIOCGCONNORDER = 90; - SIOCSIFLOG = 91; - SIOCGIFLOG = 92; - SIOCGIFDELEGATE = 93; - SIOCGIFLLADDR = 94; - SIOCGIFTYPE = 95; - SIOCGIFEXPENSIVE = 96; - SIOCSIFEXPENSIVE = 97; - SIOCGIF2KCL = 98; - SIOCSIF2KCL = 99; - SIOCGSTARTDELAY = 100; - SIOCAIFAGENTID = 101; - SIOCDIFAGENTID = 102; - SIOCGIFAGENTIDS = 103; - SIOCGIFAGENTDATA = 104; - SIOCGIFAGENTIDS32 = 105; - SIOCGIFAGENTIDS64 = 106; - SIOCGIFAGENTDATA32 = 107; - SIOCGIFAGENTDATA64 = 108; - SIOCSIFINTERFACESTATE = 109; - SIOCGIFINTERFACESTATE = 110; - SIOCSIFPROBECONNECTIVITY = 111; - SIOCGIFPROBECONNECTIVITY = 112; - SIOCGIFFUNCTIONALTYPE = 113; - SIOCSIFNETSIGNATURE = 114; - SIOCGIFNETSIGNATURE = 115; - SIOCGECNMODE = 116; - SIOCSECNMODE = 117; - SIOCSIFORDER = 118; - SIOCGIFORDER = 119; - SIOCSQOSMARKINGMODE = 120; - SIOCSFASTLANECAPABLE = 121; - SIOCSQOSMARKINGENABLED = 122; - SIOCSFASTLEENABLED = 123; - SIOCGQOSMARKINGMODE = 124; - SIOCGQOSMARKINGENABLED = 125; - SIOCSIFTIMESTAMPENABLE = 126; - SIOCSIFTIMESTAMPDISABLE = 127; - SIOCGIFTIMESTAMPENABLED = 128; - SIOCSIFDISABLEOUTPUT = 129; - SIOCGIFAGENTLIST = 130; - SIOCGIFAGENTLIST32 = 131; - SIOCGIFAGENTLIST64 = 132; - SIOCSIFLOWINTERNET = 133; - SIOCGIFLOWINTERNET = 134; - SIOCGIFNAT64PREFIX = 135; - SIOCSIFNAT64PREFIX = 136; - SIOCGIFNEXUS = 137; - SIOCSIFADDR_IN6 = 138; - SIOCGIFADDR_IN6 = 139; - SIOCSIFDSTADDR_IN6 = 140; - SIOCSIFNETMASK_IN6 = 141; - SIOCGIFDSTADDR_IN6 = 142; - SIOCGIFNETMASK_IN6 = 143; - SIOCDIFADDR_IN6 = 144; - SIOCAIFADDR_IN6 = 145; - SIOCAIFADDR_IN6_32 = 146; - SIOCAIFADDR_IN6_64 = 147; - SIOCSIFPHYADDR_IN6 = 148; - SIOCSIFPHYADDR_IN6_32 = 149; - SIOCSIFPHYADDR_IN6_64 = 150; - SIOCGIFPSRCADDR_IN6 = 151; - SIOCGIFPDSTADDR_IN6 = 152; - SIOCGIFAFLAG_IN6 = 153; - SIOCGDRLST_IN6 = 154; - SIOCGDRLST_IN6_32 = 155; - SIOCGDRLST_IN6_64 = 156; - SIOCGPRLST_IN6 = 157; - SIOCGPRLST_IN6_32 = 158; - SIOCGPRLST_IN6_64 = 159; - OSIOCGIFINFO_IN6 = 160; - SIOCGIFINFO_IN6 = 161; - SIOCSNDFLUSH_IN6 = 162; - SIOCGNBRINFO_IN6 = 163; - SIOCGNBRINFO_IN6_32 = 164; - SIOCGNBRINFO_IN6_64 = 165; - SIOCSPFXFLUSH_IN6 = 166; - SIOCSRTRFLUSH_IN6 = 167; - SIOCGIFALIFETIME_IN6 = 168; - SIOCSIFALIFETIME_IN6 = 169; - SIOCGIFSTAT_IN6 = 170; - SIOCGIFSTAT_ICMP6 = 171; - SIOCSDEFIFACE_IN6 = 172; - SIOCGDEFIFACE_IN6 = 173; - SIOCSDEFIFACE_IN6_32 = 174; - SIOCSDEFIFACE_IN6_64 = 175; - SIOCGDEFIFACE_IN6_32 = 176; - SIOCGDEFIFACE_IN6_64 = 177; - SIOCSIFINFO_FLAGS = 178; - SIOCSSCOPE6 = 179; - SIOCGSCOPE6 = 180; - SIOCGSCOPE6DEF = 181; - SIOCSIFPREFIX_IN6 = 182; - SIOCGIFPREFIX_IN6 = 183; - SIOCDIFPREFIX_IN6 = 184; - SIOCAIFPREFIX_IN6 = 185; - SIOCCIFPREFIX_IN6 = 186; - SIOCSGIFPREFIX_IN6 = 187; - SIOCAADDRCTL_POLICY = 188; - SIOCDADDRCTL_POLICY = 189; - SIOCPROTOATTACH_IN6 = 190; - SIOCPROTOATTACH_IN6_32 = 191; - SIOCPROTOATTACH_IN6_64 = 192; - SIOCPROTODETACH_IN6 = 193; - SIOCLL_START = 194; - SIOCLL_START_32 = 195; - SIOCLL_START_64 = 196; - SIOCLL_STOP = 197; - SIOCAUTOCONF_START = 198; - SIOCAUTOCONF_STOP = 199; - SIOCDRADD_IN6 = 200; - SIOCDRADD_IN6_32 = 201; - SIOCDRADD_IN6_64 = 202; - SIOCDRDEL_IN6 = 203; - SIOCDRDEL_IN6_32 = 204; - SIOCDRDEL_IN6_64 = 205; - SIOCSETROUTERMODE_IN6 = 206; - SIOCLL_CGASTART = 207; - SIOCLL_CGASTART_32 = 208; - SIOCLL_CGASTART_64 = 209; - SIOCGIFCGAPREP_IN6 = 210; - SIOCSIFCGAPREP_IN6 = 211; - DIOCSTART = 212; - DIOCSTOP = 213; - DIOCADDRULE = 214; - DIOCGETSTARTERS = 215; - DIOCGETRULES = 216; - DIOCGETRULE = 217; - DIOCSTARTREF = 218; - DIOCSTOPREF = 219; - DIOCCLRSTATES = 220; - DIOCGETSTATE = 221; - DIOCSETSTATUSIF = 222; - DIOCGETSTATUS = 223; - DIOCCLRSTATUS = 224; - DIOCNATLOOK = 225; - DIOCSETDEBUG = 226; - DIOCGETSTATES = 227; - DIOCCHANGERULE = 228; - DIOCINSERTRULE = 229; - DIOCDELETERULE = 230; - DIOCSETTIMEOUT = 231; - DIOCGETTIMEOUT = 232; - DIOCADDSTATE = 233; - DIOCCLRRULECTRS = 234; - DIOCGETLIMIT = 235; - DIOCSETLIMIT = 236; - DIOCKILLSTATES = 237; - DIOCSTARTALTQ = 238; - DIOCSTOPALTQ = 239; - DIOCADDALTQ = 240; - DIOCGETALTQS = 241; - DIOCGETALTQ = 242; - DIOCCHANGEALTQ = 243; - DIOCGETQSTATS = 244; - DIOCBEGINADDRS = 245; - DIOCADDADDR = 246; - DIOCGETADDRS = 247; - DIOCGETADDR = 248; - DIOCCHANGEADDR = 249; - DIOCGETRULESETS = 250; - DIOCGETRULESET = 251; - DIOCRCLRTABLES = 252; - DIOCRADDTABLES = 253; - DIOCRDELTABLES = 254; - DIOCRGETTABLES = 255; - DIOCRGETTSTATS = 256; - DIOCRCLRTSTATS = 257; - DIOCRCLRADDRS = 258; - DIOCRADDADDRS = 259; - DIOCRDELADDRS = 260; - DIOCRSETADDRS = 261; - DIOCRGETADDRS = 262; - DIOCRGETASTATS = 263; - DIOCRCLRASTATS = 264; - DIOCRTSTADDRS = 265; - DIOCRSETTFLAGS = 266; - DIOCRINADEFINE = 267; - DIOCOSFPFLUSH = 268; - DIOCOSFPADD = 269; - DIOCOSFPGET = 270; - DIOCXBEGIN = 271; - DIOCXCOMMIT = 272; - DIOCXROLLBACK = 273; - DIOCGETSRCNODES = 274; - DIOCCLRSRCNODES = 275; - DIOCSETHOSTID = 276; - DIOCIGETIFACES = 277; - DIOCSETIFFLAG = 278; - DIOCCLRIFFLAG = 279; - DIOCKILLSRCNODES = 280; - DIOCGIFSPEED = 281; -} - -// TODO(nedwill): clean these up, see if more headers have socket options -enum SocketOptName { - option allow_alias = true; - // socket.h - SO_DEBUG = 1; // turn on debugging info recording - SO_ACCEPTCONN = 2; // socket has had listen() - SO_REUSEADDR = 4; // allow local address reuse - SO_KEEPALIVE = 8; // keep connections alive - SO_DONTROUTE = 16; // just use interface addresses - SO_BROADCAST = 32; // permit sending of broadcast msgs - SO_USELOOPBACK = 64; // bypass hardware when possible - SO_LINGER = 128; // linger on close if data present (in ticks) - // SO_LINGER_SEC = 0x1080; /* linger on close if data present (in seconds) */ - SO_OOBINLINE = 256; // leave received OOB data in line - SO_REUSEPORT = 512; // allow local address & port reuse - SO_TIMESTAMP = 1024; // timestamp received dgram traffic - SO_TIMESTAMP_MONOTONIC = 2048; // Monotonically increasing timestamp on rcvd dgram - SO_ACCEPTFILTER = 4096; // there is an accept filter - SO_DONTTRUNC = 8192; // APPLE: Retain unread data - SO_WANTMORE = 16384; // APPLE: Give hint when more data ready - SO_WANTOOBFLAG = 32768; // APPLE: Want OOB in MSG_FLAG on receive - SO_NOWAKEFROMSLEEP = 65536; // Don't wake for traffic to this socket - SO_NOAPNFALLBK = 131072; // Don't attempt APN fallback for the socket - SO_TIMESTAMP_CONTINUOUS = 262144; // Continuous monotonic timestamp on rcvd dgram - SO_SNDBUF = 4097; // send buffer size - SO_RCVBUF = 4098; // receive buffer size - SO_SNDLOWAT = 4099; // send low-water mark - SO_RCVLOWAT = 4100; // receive low-water mark - SO_SNDTIMEO = 4101; // send timeout - SO_RCVTIMEO = 4102; // receive timeout - SO_ERROR = 4103; // get error status and clear - SO_TYPE = 4104; // get socket type - SO_LABEL = 4112; // socket's MAC label - SO_PEERLABEL = 4113; // socket's peer MAC label - SO_NREAD = 4128; // APPLE: get 1st-packet byte count - SO_NKE = 4129; // APPLE: Install socket-level NKE - SO_NOSIGPIPE = 4130; // APPLE: No SIGPIPE on EPIPE - SO_NOADDRERR = 4131; // APPLE: Returns EADDRNOTAVAIL when src is not available anymore - SO_NWRITE = 4132; // APPLE: Get number of bytes currently in send socket buffer - SO_REUSESHAREUID = 4133; // APPLE: Allow reuse of port/socket by different userids - SO_NOTIFYCONFLICT = 4134; // APPLE: send notification if there is a bind on a port which is already in use - SO_UPCALLCLOSEWAIT = 4135; // APPLE: block on close until an upcall returns - SO_LINGER_SEC = 4224; // linger on close if data present (in seconds) - SO_RESTRICTIONS = 4225; // APPLE: deny flag set - // SO_RESTRICT_DENY_IN = 0x1; /* deny inbound (trapdoor) */ - // SO_RESTRICT_DENY_OUT = 0x2; /* deny outbound (trapdoor) */ - // SO_RESTRICT_DENY_CELLULAR = 0x4; /* deny use of cellular (trapdoor) */ - // SO_RESTRICT_DENY_EXPENSIVE = 0x8; /* deny use of expensive if (trapdoor) */ - SO_RANDOMPORT = 4226; // APPLE: request local port randomization - SO_NP_EXTENSIONS = 4227; // To turn off some POSIX behavior - SO_EXECPATH = 4229; // Application Firewall Socket option - SO_TRAFFIC_CLASS = 4230; // Traffic service class (int) - SO_TC_BK_SYS = 100; // lowest class - SO_TC_BK = 200; - SO_TC_BE = 0; - SO_TC_RD = 300; - SO_TC_OAM = 400; - SO_TC_AV = 500; - SO_TC_RV = 600; - SO_TC_VI = 700; - SO_TC_VO = 800; - SO_TC_CTL = 900; // highest class - SO_TC_MAX = 10; // Total # of traffic classes - SO_TC_UNSPEC = -1; // Traffic class not specified - SO_RECV_TRAFFIC_CLASS = 4231; // Receive traffic class (bool) - SO_TRAFFIC_CLASS_DBG = 4232; // Debug traffic class (struct so_tcdbg) - SO_TRAFFIC_CLASS_STATS = 4233; // Traffic class statistics - SO_PRIVILEGED_TRAFFIC_CLASS = 4240; // Privileged traffic class (bool) - SO_DEFUNCTIT = 4241; // Defunct a socket (only in internal builds) - SO_DEFUNCTOK = 4352; // can be defunct'd - SO_ISDEFUNCT = 4353; // get defunct status - SO_OPPORTUNISTIC = 4354; // deprecated; use SO_TRAFFIC_CLASS - SO_FLUSH = 4355; // flush unsent data (int) - SO_RECV_ANYIF = 4356; // unrestricted inbound processing - SO_TRAFFIC_MGT_BACKGROUND = 4357; // Background traffic management - SO_FLOW_DIVERT_TOKEN = 4358; // flow divert token - SO_DELEGATED = 4359; // set socket as delegate (pid_t) - SO_DELEGATED_UUID = 4360; // set socket as delegate (uuid_t) - SO_NECP_ATTRIBUTES = 4361; // NECP socket attributes (domain, account, etc.) - SO_CFIL_SOCK_ID = 4368; // get content filter socket ID (cfil_sock_id_t) - SO_NECP_CLIENTUUID = 4369; // NECP Client uuid - SO_NUMRCVPKT = 4370; // number of datagrams in receive socket buffer - SO_AWDL_UNRESTRICTED = 4371; // try to use AWDL in restricted mode - SO_EXTENDED_BK_IDLE = 4372; // extended time to keep socket idle after app is suspended (int) - SO_MARK_CELLFALLBACK = 4373; // Mark as initiated by cell fallback - SO_NET_SERVICE_TYPE = 4374; // Network service type - SO_QOSMARKING_POLICY_OVERRIDE = 4375; // int - SO_INTCOPROC_ALLOW = 4376; // Try to use internal co-processor interfaces. - SO_TC_NET_SERVICE_OFFSET = 10000; - SO_NETSVC_MARKING_LEVEL = 4377; // Get QoS marking in effect for socket - // tcp.h - TCPOPT_EOL = 0; - TCPOPT_NOP = 1; - TCPOPT_MAXSEG = 2; - TCPOLEN_MAXSEG = 4; - TCPOPT_WINDOW = 3; - TCPOLEN_WINDOW = 3; - TCPOPT_SACK_PERMITTED = 4; // Experimental - TCPOLEN_SACK_PERMITTED = 2; - TCPOPT_SACK = 5; // Experimental - TCPOLEN_SACK = 8; // len of sack block - TCPOPT_TIMESTAMP = 8; - TCPOLEN_TIMESTAMP = 10; - MAX_TCPOPTLEN = 40; // Absolute maximum TCP options len - TCPOPT_CC = 11; // CC options: RFC-1644 - TCPOPT_CCNEW = 12; - TCPOPT_CCECHO = 13; - TCPOLEN_CC = 6; - TCPOPT_SIGNATURE = 19; // Keyed MD5: RFC 2385 - TCPOLEN_SIGNATURE = 18; - TCPOPT_MULTIPATH = 30; - TCPOPT_FASTOPEN = 34; - TCPOLEN_FASTOPEN_REQ = 2; - MAX_SACK_BLKS = 6; // Max # SACK blocks stored at sender side - TCP_MAX_SACK = 4; // MAX # SACKs sent in any segment - TCP_MSS = 512; - TCP_MINMSS = 216; - TCP6_MSS = 1024; - TCP_MAXWIN = 65535; // largest value for (unscaled) window - TTCP_CLIENT_SND_WND = 4096; // dflt send window for T/TCP client - TCP_MAX_WINSHIFT = 14; // maximum window shift - TCP_NODELAY = 1; // don't delay send to coalesce packets - TCP_MAXSEG = 2; // set maximum segment size - TCP_NOPUSH = 4; // don't push last block of write - TCP_NOOPT = 8; // don't use TCP options - TCP_KEEPALIVE = 16; // idle time used when SO_KEEPALIVE is enabled - TCP_CONNECTIONTIMEOUT = 32; // connection timeout - PERSIST_TIMEOUT = 64; - TCP_RXT_CONNDROPTIME = 128; - TCP_RXT_FINDROP = 256; - TCP_KEEPINTVL = 257; // interval between keepalives - TCP_KEEPCNT = 258; // number of keepalives before close - TCP_SENDMOREACKS = 259; // always ack every other packet - TCP_ENABLE_ECN = 260; // Enable ECN on a connection - TCP_FASTOPEN = 261; // Enable/Disable TCP Fastopen on this socket - TCP_CONNECTION_INFO = 262; // State of TCP connection - TCP_INFO = 512; // retrieve tcp_info structure - TCP_MEASURE_SND_BW = 514; // Measure sender's bandwidth for this connection - TCP_NOTSENT_LOWAT = 513; // Low water mark for TCP unsent data - TCP_MEASURE_BW_BURST = 515; // Burst size to use for bandwidth measurement - TCP_PEER_PID = 516; // Lookup pid of the process we're connected to - TCP_ADAPTIVE_READ_TIMEOUT = 517; // Read timeout used as a multiple of RTT - TCP_ENABLE_MSGS = 518; - TCP_ADAPTIVE_WRITE_TIMEOUT = 519; // Write timeout used as a multiple of RTT - TCP_NOTIMEWAIT = 520; // Avoid going into time-wait - TCP_DISABLE_BLACKHOLE_DETECTION = 521; // disable PMTU blackhole detection - TCP_ECN_MODE = 528; // fine grain control for A/B testing - TCP_KEEPALIVE_OFFLOAD = 529; // offload keep alive processing to firmware - ECN_MODE_DEFAULT = 0; // per interface or system wide default - ECN_MODE_ENABLE = 1; // force enable ECN on connection - ECN_MODE_DISABLE = 2; // force disable ECN on connection - TCP_MAX_NOTIFY_ACK = 10; - TCP_NOTIFY_ACKNOWLEDGEMENT = 530; // Notify when data is acknowledged - MPTCP_SERVICE_TYPE = 531; // MPTCP Service type - TCP_FASTOPEN_FORCE_HEURISTICS = 532; // Make sure TFO-heuristics never get disabled - MPTCP_SVCTYPE_HANDOVER = 0; // Default 0 - MPTCP_SVCTYPE_INTERACTIVE = 1; - MPTCP_SVCTYPE_AGGREGATE = 2; - MPTCP_SVCTYPE_MAX = 3; - TCP_RXT_MINIMUM_TIMEOUT = 533; - MPTCP_ALTERNATE_PORT = 534; - TCPI_OPT_TIMESTAMPS = 1; - TCPI_OPT_SACK = 2; - TCPI_OPT_WSCALE = 4; - TCPI_OPT_ECN = 8; - TCPI_FLAG_LOSSRECOVERY = 1; // Currently in loss recovery - TCPI_FLAG_STREAMING_ON = 2; // Streaming detection on - CONNINFO_MPTCP_VERSION = 3; - MPTCP_ITFSTATS_SIZE = 4; - MPTCPCI_FIRSTPARTY = 1; - TCPCI_OPT_TIMESTAMPS = 1; // Timestamps enabled - TCPCI_OPT_SACK = 2; // SACK enabled - TCPCI_OPT_WSCALE = 4; // Window scaling enabled - TCPCI_OPT_ECN = 8; // ECN enabled - TCPCI_FLAG_LOSSRECOVERY = 1; - TCPCI_FLAG_REORDERING_DETECTED = 2; - // in.h - IP_OPTIONS = 1; // buf/ip_opts; set/get IP options - IP_HDRINCL = 2; // int; header is included with data - IP_TOS = 3; // int; IP type of service and preced. - IP_TTL = 4; // int; IP time to live - IP_RECVOPTS = 5; // bool; receive all IP opts w/dgram - IP_RECVRETOPTS = 6; // bool; receive IP opts for response - IP_RECVDSTADDR = 7; // bool; receive IP dst addr w/dgram - IP_RETOPTS = 8; // ip_opts; set/get IP options - IP_MULTICAST_IF = 9; // u_char; set/get IP multicast i/f - IP_MULTICAST_TTL = 10; // u_char; set/get IP multicast ttl - IP_MULTICAST_LOOP = 11; // u_char; set/get IP multicast loopback - IP_ADD_MEMBERSHIP = 12; // ip_mreq; add an IP group membership - IP_DROP_MEMBERSHIP = 13; // ip_mreq; drop an IP group membership - IP_MULTICAST_VIF = 14; // set/get IP mcast virt. iface - IP_RSVP_ON = 15; // enable RSVP in kernel - IP_RSVP_OFF = 16; // disable RSVP in kernel - IP_RSVP_VIF_ON = 17; // set RSVP per-vif socket - IP_RSVP_VIF_OFF = 18; // unset RSVP per-vif socket - IP_PORTRANGE = 19; // int; range to choose for unspec port - IP_RECVIF = 20; // bool; receive reception if w/dgram - IP_IPSEC_POLICY = 21; // int; set/get security policy - IP_FAITH = 22; // deprecated - IP_STRIPHDR = 23; // bool: drop receive of raw IP header - IP_RECVTTL = 24; // bool; receive reception TTL w/dgram - IP_BOUND_IF = 25; // int; set/get bound interface - IP_PKTINFO = 26; // get pktinfo on recv socket, set src on sent dgram - IP_RECVTOS = 27; // bool; receive IP TOS w/dgram - IP_FW_ADD = 40; // add a firewall rule to chain - IP_FW_DEL = 41; // delete a firewall rule from chain - IP_FW_FLUSH = 42; // flush firewall rule chain - IP_FW_ZERO = 43; // clear single/all firewall counter(s) - IP_FW_GET = 44; // get entire firewall rule chain - IP_FW_RESETLOG = 45; // reset logging counters - IP_OLD_FW_ADD = 50; // add a firewall rule to chain - IP_OLD_FW_DEL = 51; // delete a firewall rule from chain - IP_OLD_FW_FLUSH = 52; // flush firewall rule chain - IP_OLD_FW_ZERO = 53; // clear single/all firewall counter(s) - IP_OLD_FW_GET = 54; // get entire firewall rule chain - IP_NAT__XXX = 55; // set/get NAT opts XXX Deprecated, do not use - IP_OLD_FW_RESETLOG = 56; // reset logging counters - IP_DUMMYNET_CONFIGURE = 60; // add/configure a dummynet pipe - IP_DUMMYNET_DEL = 61; // delete a dummynet pipe from chain - IP_DUMMYNET_FLUSH = 62; // flush dummynet - IP_DUMMYNET_GET = 64; // get entire dummynet pipes - IP_TRAFFIC_MGT_BACKGROUND = 65; // int*; get background IO flags; set background IO - IP_MULTICAST_IFINDEX = 66; // int*; set/get IP multicast i/f index - IP_ADD_SOURCE_MEMBERSHIP = 70; // join a source-specific group - IP_DROP_SOURCE_MEMBERSHIP = 71; // drop a single source - IP_BLOCK_SOURCE = 72; // block a source - IP_UNBLOCK_SOURCE = 73; // unblock a source - IP_MSFILTER = 74; // set/get filter list - MCAST_JOIN_GROUP = 80; // join an any-source group - MCAST_LEAVE_GROUP = 81; // leave all sources for group - MCAST_JOIN_SOURCE_GROUP = 82; // join a source-specific group - MCAST_LEAVE_SOURCE_GROUP = 83; // leave a single source - MCAST_BLOCK_SOURCE = 84; // block a source - MCAST_UNBLOCK_SOURCE = 85; // unblock a source - IP_FORCE_OUT_IFP = 69; // not implemented; use IP_BOUND_IF instead - IP_NO_IFT_CELLULAR = 6969; // for internal use only - IP_OUT_IF = 9696; // for internal use only - IP_DEFAULT_MULTICAST_TTL = 1; // normally limit m'casts to 1 hop - IP_DEFAULT_MULTICAST_LOOP = 1; // normally hear sends if a member - IP_MIN_MEMBERSHIPS = 31; - IP_MAX_MEMBERSHIPS = 4095; - IP_MAX_GROUP_SRC_FILTER = 512; // sources per group - IP_MAX_SOCK_SRC_FILTER = 128; // sources per socket/group - IP_MAX_SOCK_MUTE_FILTER = 128; // XXX no longer used - MCAST_INCLUDE = 1; // fmode: include these source(s) - MCAST_EXCLUDE = 2; // fmode: exclude these source(s) - IP_PORTRANGE_DEFAULT = 0; // default range - IP_PORTRANGE_HIGH = 1; // "high" - request firewall bypass - IP_PORTRANGE_LOW = 2; // "low" - vouchsafe security - IPCTL_FORWARDING = 1; // act as router - IPCTL_SENDREDIRECTS = 2; // may send redirects when forwarding - IPCTL_DEFTTL = 3; // default TTL - IPCTL_DEFMTU = 4; // default MTU - IPCTL_RTEXPIRE = 5; // cloned route expiration time - IPCTL_RTMINEXPIRE = 6; // min value for expiration time - IPCTL_RTMAXCACHE = 7; // trigger level for dynamic expire - IPCTL_SOURCEROUTE = 8; // may perform source routes - IPCTL_DIRECTEDBROADCAST = 9; // may re-broadcast received packets - IPCTL_INTRQMAXLEN = 10; // max length of netisr queue - IPCTL_INTRQDROPS = 11; // number of netisr q drops - IPCTL_STATS = 12; // ipstat structure - IPCTL_ACCEPTSOURCEROUTE = 13; // may accept source routed packets - IPCTL_FASTFORWARDING = 14; // use fast IP forwarding code - IPCTL_KEEPFAITH = 15; // deprecated - IPCTL_GIF_TTL = 16; // default TTL for gif encap packet - IPCTL_MAXID = 17; - _DSCP_DF = 0; // RFC 2474 - _DSCP_CS0 = 0; // RFC 2474 - _DSCP_CS1 = 8; // RFC 2474 - _DSCP_CS2 = 16; // RFC 2474 - _DSCP_CS3 = 24; // RFC 2474 - _DSCP_CS4 = 32; // RFC 2474 - _DSCP_CS5 = 40; // RFC 2474 - _DSCP_CS6 = 48; // RFC 2474 - _DSCP_CS7 = 56; // RFC 2474 - _DSCP_EF = 46; // RFC 2474 - _DSCP_VA = 44; // RFC 5865 - _DSCP_AF11 = 10; // RFC 2597 - _DSCP_AF12 = 12; // RFC 2597 - _DSCP_AF13 = 14; // RFC 2597 - _DSCP_AF21 = 18; // RFC 2597 - _DSCP_AF22 = 20; // RFC 2597 - _DSCP_AF23 = 22; // RFC 2597 - _DSCP_AF31 = 26; // RFC 2597 - _DSCP_AF32 = 28; // RFC 2597 - _DSCP_AF33 = 30; // RFC 2597 - _DSCP_AF41 = 34; // RFC 2597 - _DSCP_AF42 = 36; // RFC 2597 - _DSCP_AF43 = 38; // RFC 2597 - _DSCP_52 = 52; // Wi-Fi WMM Certification: Sigma - _MAX_DSCP = 63; // coded on 6 bits - // in6.h - IPV6_OPTIONS = 1; // buf/ip6_opts; set/get IP6 options - IPV6_RECVOPTS = 5; // bool; receive all IP6 opts w/dgram - IPV6_RECVRETOPTS = 6; // bool; receive IP6 opts for response - IPV6_RECVDSTADDR = 7; // bool; receive IP6 dst addr w/dgram - IPV6_RETOPTS = 8; // ip6_opts; set/get IP6 options - IPV6_SOCKOPT_RESERVED1 = 3; // reserved for future use - IPV6_UNICAST_HOPS = 4; // int; IP6 hops - IPV6_MULTICAST_IF = 9; // __uint8_t; set/get IP6 multicast i/f - IPV6_MULTICAST_HOPS = 10; // __uint8_t; set/get IP6 multicast hops - IPV6_MULTICAST_LOOP = 11; // __uint8_t; set/get IP6 mcast loopback - IPV6_JOIN_GROUP = 12; // ip6_mreq; join a group membership - IPV6_LEAVE_GROUP = 13; // ip6_mreq; leave a group membership - IPV6_PORTRANGE = 14; // int; range to choose for unspec port - ICMP6_FILTER = 18; // icmp6_filter; icmp6 filter - IPV6_2292PKTINFO = 19; // bool; send/recv if, src/dst addr - IPV6_2292HOPLIMIT = 20; // bool; hop limit - IPV6_2292NEXTHOP = 21; // bool; next hop addr - IPV6_2292HOPOPTS = 22; // bool; hop-by-hop option - IPV6_2292DSTOPTS = 23; // bool; destinaion option - IPV6_2292RTHDR = 24; // ip6_rthdr: routing header - IPV6_2292PKTOPTIONS = 25; - IPV6_CHECKSUM = 26; // int; checksum offset for raw socket - IPV6_V6ONLY = 27; // bool; only bind INET6 at wildcard bind - IPV6_IPSEC_POLICY = 28; // struct; get/set security policy - IPV6_FAITH = 29; // deprecated - IPV6_FW_ADD = 30; // add a firewall rule to chain - IPV6_FW_DEL = 31; // delete a firewall rule from chain - IPV6_FW_FLUSH = 32; // flush firewall rule chain - IPV6_FW_ZERO = 33; // clear single/all firewall counter(s) - IPV6_FW_GET = 34; // get entire firewall rule chain - IPV6_RECVTCLASS = 35; // bool; recv traffic class values - IPV6_TCLASS = 36; // int; send traffic class value - IPV6_RTHDRDSTOPTS = 57; - IPV6_RECVPKTINFO = 61; - IPV6_RECVHOPLIMIT = 37; // bool; recv hop limit - IPV6_RECVRTHDR = 38; // bool; recv routing header - IPV6_RECVHOPOPTS = 39; // bool; recv hop-by-hop option - IPV6_RECVDSTOPTS = 40; // bool; recv dst option after rthdr - IPV6_RECVRTHDRDSTOPTS = 41; // bool; recv dst option before rthdr - IPV6_USE_MIN_MTU = 42; // bool; send packets at the minimum MTU - IPV6_RECVPATHMTU = 43; // bool; notify an according MTU - IPV6_PATHMTU = 44; - IPV6_REACHCONF = 45; - IPV6_3542PKTINFO = 46; // in6_pktinfo; send if, src addr - IPV6_3542HOPLIMIT = 47; // int; send hop limit - IPV6_3542NEXTHOP = 48; // sockaddr; next hop addr - IPV6_3542HOPOPTS = 49; // ip6_hbh; send hop-by-hop option - IPV6_3542DSTOPTS = 50; // ip6_dest; send dst option befor rthdr - IPV6_3542RTHDR = 51; // ip6_rthdr; send routing header - IPV6_AUTOFLOWLABEL = 59; // bool; attach flowlabel automagically - IPV6_DONTFRAG = 62; // bool; disable IPv6 fragmentation - IPV6_PREFER_TEMPADDR = 63; - IPV6_MSFILTER = 74; // struct __msfilterreq; - IPV6_BOUND_IF = 125; // int; set/get bound interface - IPV6_NO_IFT_CELLULAR = 6969; // for internal use only - IPV6_OUT_IF = 9696; // for internal use only - IPV6_RTHDR_LOOSE = 0; // this hop need not be a neighbor. - IPV6_RTHDR_STRICT = 1; // this hop must be a neighbor. - IPV6_RTHDR_TYPE_0_SOCKET = 0; // IPv6 routing header type 0 - IPV6_DEFAULT_MULTICAST_HOPS = 1; // normally limit m'casts to 1 hop - IPV6_DEFAULT_MULTICAST_LOOP = 1; // normally hear sends if a member - IPV6_MIN_MEMBERSHIPS = 31; - IPV6_MAX_MEMBERSHIPS = 4095; - IPV6_MAX_GROUP_SRC_FILTER = 512; // sources per group - IPV6_MAX_SOCK_SRC_FILTER = 128; // sources per socket/group - IPV6_PORTRANGE_DEFAULT = 0; // default range - IPV6_PORTRANGE_HIGH = 1; // "high" - request firewall bypass - IPV6_PORTRANGE_LOW = 2; // "low" - vouchsafe security - IPV6CTL_FORWARDING = 1; // act as router - IPV6CTL_SENDREDIRECTS = 2; // may send redirects when forwarding - IPV6CTL_DEFHLIM = 3; // default Hop-Limit - IPV6CTL_DEFMTU = 4; // default MTU - IPV6CTL_FORWSRCRT = 5; // forward source-routed dgrams - IPV6CTL_STATS = 6; // stats - IPV6CTL_MRTSTATS = 7; // multicast forwarding stats - IPV6CTL_MRTPROTO = 8; // multicast routing protocol - IPV6CTL_MAXFRAGPACKETS = 9; // max packets reassembly queue - IPV6CTL_SOURCECHECK = 10; // verify source route and intf - IPV6CTL_SOURCECHECK_LOGINT = 11; // minimume logging interval - IPV6CTL_ACCEPT_RTADV = 12; - IPV6CTL_KEEPFAITH = 13; // deprecated - IPV6CTL_LOG_INTERVAL = 14; - IPV6CTL_HDRNESTLIMIT = 15; - IPV6CTL_DAD_COUNT = 16; - IPV6CTL_AUTO_FLOWLABEL = 17; - IPV6CTL_DEFMCASTHLIM = 18; - IPV6CTL_GIF_HLIM = 19; // default HLIM for gif encap packet - IPV6CTL_KAME_VERSION = 20; - IPV6CTL_USE_DEPRECATED = 21; // use deprec addr (RFC2462 5.5.4) - IPV6CTL_RR_PRUNE = 22; // walk timer for router renumbering - IPV6CTL_MAPPED_ADDR = 23; - IPV6CTL_V6ONLY = 24; - IPV6CTL_RTEXPIRE = 25; // cloned route expiration time - IPV6CTL_RTMINEXPIRE = 26; // min value for expiration time - IPV6CTL_RTMAXCACHE = 27; // trigger level for dynamic expire - IPV6CTL_USETEMPADDR = 32; // use temporary addresses [RFC 4941] - IPV6CTL_TEMPPLTIME = 33; // preferred lifetime for tmpaddrs - IPV6CTL_TEMPVLTIME = 34; // valid lifetime for tmpaddrs - IPV6CTL_AUTO_LINKLOCAL = 35; // automatic link-local addr assign - IPV6CTL_RIP6STATS = 36; // raw_ip6 stats - IPV6CTL_PREFER_TEMPADDR = 37; // prefer temporary addr as src - IPV6CTL_ADDRCTLPOLICY = 38; // get/set address selection policy - IPV6CTL_USE_DEFAULTZONE = 39; // use default scope zone - IPV6CTL_MAXFRAGS = 41; // max fragments - IPV6CTL_MCAST_PMTU = 44; // enable pMTU discovery for mcast? - IPV6CTL_NEIGHBORGCTHRESH = 46; - IPV6CTL_MAXIFPREFIXES = 47; - IPV6CTL_MAXIFDEFROUTERS = 48; - IPV6CTL_MAXDYNROUTES = 49; - ICMPV6CTL_ND6_ONLINKNSRFC4861 = 50; - IPV6CTL_MAXID = 51; -} - -message SetSocketOpt { - optional Protocol level = 1; - optional SocketOptName name = 2; - // TODO(nedwill): structure for val - optional bytes val = 3; - optional FileDescriptor fd = 4; -} - -// Some pre-allocated file descriptors to make guessing -// a real file descriptor more likely. -// TODO(nedwill): do experiments to determine how many of -// these to use. -enum FileDescriptor { - FD_0 = 0; - FD_1 = 1; - FD_2 = 2; - FD_3 = 3; - FD_4 = 4; - FD_5 = 5; - FD_6 = 6; - FD_7 = 7; - FD_8 = 8; - FD_9 = 9; -} - -message GetSocketOpt { - optional Protocol level = 1; - optional SocketOptName name = 2; - optional int32 size = 3; - optional uint32 fd = 4; -} - -message Socket { - required Domain domain = 1; - required SoType so_type = 2; - required Protocol protocol = 3; - // TODO: options, e.g. SO_ACCEPTCONN - -} - -enum Domain { - AF_UNSPEC = 0; - AF_UNIX = 1; - AF_INET = 2; - AF_IMPLINK = 3; - AF_PUP = 4; - AF_CHAOS = 5; - AF_NS = 6; - AF_ISO = 7; - AF_ECMA = 8; - AF_DATAKIT = 9; - AF_CCITT = 10; - AF_SNA = 11; - AF_DECnet = 12; - AF_DLI = 13; - AF_LAT = 14; - AF_HYLINK = 15; - AF_APPLETALK = 16; - AF_ROUTE = 17; - AF_LINK = 18; - pseudo_AF_XTP = 19; - AF_COIP = 20; - AF_CNT = 21; - pseudo_AF_RTIP = 22; - AF_IPX = 23; - AF_SIP = 24; - pseudo_AF_PIP = 25; - AF_NDRV = 27; - AF_ISDN = 28; - pseudo_AF_KEY = 29; - AF_INET6 = 30; - AF_NATM = 31; - AF_SYSTEM = 32; - AF_NETBIOS = 33; - AF_PPP = 34; - pseudo_AF_HDRCMPLT = 35; - AF_RESERVED_36 = 36; - AF_IEEE80211 = 37; - AF_UTUN = 38; - AF_MULTIPATH = 39; - AF_MAX = 40; -} - -enum SoType { - SOCK_STREAM = 1; - SOCK_DGRAM = 2; - SOCK_RAW = 3; - SOCK_RDM = 4; - SOCK_SEQPACKET = 5; -} - -enum Protocol { - IPPROTO_IP = 0; - IPPROTO_ICMP = 1; - IPPROTO_IGMP = 2; - IPPROTO_GGP = 3; - IPPROTO_IPV4 = 4; - IPPROTO_TCP = 6; - IPPROTO_ST = 7; - IPPROTO_EGP = 8; - IPPROTO_PIGP = 9; - IPPROTO_RCCMON = 10; - IPPROTO_NVPII = 11; - IPPROTO_PUP = 12; - IPPROTO_ARGUS = 13; - IPPROTO_EMCON = 14; - IPPROTO_XNET = 15; - IPPROTO_CHAOS = 16; - IPPROTO_UDP = 17; - IPPROTO_MUX = 18; - IPPROTO_MEAS = 19; - IPPROTO_HMP = 20; - IPPROTO_PRM = 21; - IPPROTO_IDP = 22; - IPPROTO_TRUNK1 = 23; - IPPROTO_TRUNK2 = 24; - IPPROTO_LEAF1 = 25; - IPPROTO_LEAF2 = 26; - IPPROTO_RDP = 27; - IPPROTO_IRTP = 28; - IPPROTO_TP = 29; - IPPROTO_BLT = 30; - IPPROTO_NSP = 31; - IPPROTO_INP = 32; - IPPROTO_SEP = 33; - IPPROTO_3PC = 34; - IPPROTO_IDPR = 35; - IPPROTO_XTP = 36; - IPPROTO_DDP = 37; - IPPROTO_CMTP = 38; - IPPROTO_TPXX = 39; - IPPROTO_IL = 40; - IPPROTO_IPV6 = 41; - IPPROTO_SDRP = 42; - IPPROTO_ROUTIN = 43; - IPPROTO_FRAGMEN = 44; - IPPROTO_IDRP = 45; - IPPROTO_RSVP = 46; - IPPROTO_GRE = 47; - IPPROTO_MHRP = 48; - IPPROTO_BHA = 49; - IPPROTO_ESP = 50; - IPPROTO_AH = 51; - IPPROTO_INLSP = 52; - IPPROTO_SWIPE = 53; - IPPROTO_NHRP = 54; - IPPROTO_ICMPV6 = 58; - IPPROTO_NONE = 59; - IPPROTO_DSTOPTS = 60; - IPPROTO_AHIP = 61; - IPPROTO_CFTP = 62; - IPPROTO_HELLO = 63; - IPPROTO_SATEXPA = 64; - IPPROTO_KRYPTOLA = 65; - IPPROTO_RVD = 66; - IPPROTO_IPPC = 67; - IPPROTO_ADFS = 68; - IPPROTO_SATMON = 69; - IPPROTO_VISA = 70; - IPPROTO_IPCV = 71; - IPPROTO_CPNX = 72; - IPPROTO_CPHB = 73; - IPPROTO_WSN = 74; - IPPROTO_PVP = 75; - IPPROTO_BRSATMO = 76; - IPPROTO_ND = 77; - IPPROTO_WBMON = 78; - IPPROTO_WBEXPAK = 79; - IPPROTO_EON = 80; - IPPROTO_VMTP = 81; - IPPROTO_SVMTP = 82; - IPPROTO_VINES = 83; - IPPROTO_TTP = 84; - IPPROTO_IGP = 85; - IPPROTO_DGP = 86; - IPPROTO_TCF = 87; - IPPROTO_IGRP = 88; - IPPROTO_OSPFIGP = 89; - IPPROTO_SRPC = 90; - IPPROTO_LARP = 91; - IPPROTO_MTP = 92; - IPPROTO_AX25 = 93; - IPPROTO_IPEIP = 94; - IPPROTO_MICP = 95; - IPPROTO_SCCSP = 96; - IPPROTO_ETHERIP = 97; - IPPROTO_ENCAP = 98; - IPPROTO_APES = 99; - IPPROTO_GMTP = 100; - IPPROTO_PIM = 103; - IPPROTO_IPCOMP = 108; - IPPROTO_PGM = 113; - IPPROTO_SCTP = 132; - IPPROTO_DIVERT = 254; - IPPROTO_RAW = 255; - IPPROTO_MAX = 256; - IPPROTO_DONE = 257; - SOL_SOCKET = 65535; // options for socket level -} diff --git a/fuzz/proto/BUILD.bazel b/fuzz/proto/BUILD.bazel new file mode 100644 index 0000000..1c3a030 --- /dev/null +++ b/fuzz/proto/BUILD.bazel @@ -0,0 +1,168 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto_grpc//python:defs.bzl", "python_proto_library") + +proto_library( + name = "bsd_enums_proto", + srcs = [ + "bsd_enums.proto", + ], + visibility = [ + "//fuzz/handlers:__pkg__", + "//tools/generate_syscall:__pkg__", + ], +) + +proto_library( + name = "bsd_types_proto", + srcs = [ + "bsd_types.proto", + ], + visibility = [ + "//fuzz/handlers:__pkg__", + "//tools/generate_syscall:__pkg__", + ], + deps = [":bsd_enums_proto"], +) + +proto_library( + name = "bsd_proto", + srcs = ["bsd.proto"], + visibility = [ + "//fuzz/handlers:__pkg__", + "//tools/generate_syscall:__pkg__", + ], + deps = [ + ":bsd_enums_proto", + ":bsd_types_proto", + ], +) + +cc_proto_library( + name = "bsd_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = [":bsd_proto"], +) + +cc_proto_library( + name = "bsd_syscalls_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = ["//tools/generate_syscall:bsd_syscalls_proto"], +) + +proto_library( + name = "common_proto", + srcs = ["common.proto"], + visibility = ["//fuzz/handlers:__pkg__"], +) + +proto_library( + name = "mach_message_proto", + srcs = ["mach_message.proto"], + visibility = [ + "//fuzz/handlers:__pkg__", + "//third_party/bootstrap_cmds:__pkg__", + ], + deps = [":common_proto"], +) + +cc_proto_library( + name = "mach_message_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = [":mach_message_proto"], +) + +cc_proto_library( + name = "mig_generated_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = ["//third_party/bootstrap_cmds:mig_generated_proto"], +) + +proto_library( + name = "mach_traps_proto", + srcs = ["mach_traps.proto"], + visibility = ["//fuzz/handlers:__pkg__"], + deps = [ + ":mach_message_proto", + "//third_party/bootstrap_cmds:mig_generated_proto", + ], +) + +cc_proto_library( + name = "mach_traps_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = [":mach_traps_proto"], +) + +proto_library( + name = "mig_types_proto", + srcs = ["mig_types.proto"], + visibility = [ + "//fuzz/handlers:__pkg__", + "//third_party/bootstrap_cmds:__pkg__", + ], + deps = [":common_proto"], +) + +cc_proto_library( + name = "mig_types_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = [":mig_types_proto"], +) + +proto_library( + name = "schedule_proto", + srcs = ["schedule.proto"], + visibility = ["//fuzz/handlers:__pkg__"], +) + +cc_proto_library( + name = "schedule_cc_proto", + visibility = ["//fuzz/handlers:__pkg__"], + deps = [":schedule_proto"], +) + +proto_library( + name = "session_proto", + srcs = [ + "session.proto", + ], + deps = [ + "//fuzz/proto:bsd_proto", + "//fuzz/proto:common_proto", + "//fuzz/proto:mach_message_proto", + "//fuzz/proto:mach_traps_proto", + "//fuzz/proto:schedule_proto", + "//tools/generate_syscall:bsd_syscalls_proto", + ], +) + +# Annoyingly, py_proto_library is not provided by bazel officially so we need +# to use the gRPC rule which requires duplicating the proto_libaries. +python_proto_library( + name = "session_py_proto", + protos = [ + ":session_proto", + "//fuzz/proto:bsd_enums_proto", + "//fuzz/proto:bsd_proto", + "//fuzz/proto:bsd_types_proto", + "//fuzz/proto:common_proto", + "//fuzz/proto:mach_message_proto", + "//fuzz/proto:mach_traps_proto", + "//fuzz/proto:mig_types_proto", + "//fuzz/proto:schedule_proto", + "//third_party/bootstrap_cmds:mig_generated_proto", + "//tools/generate_syscall:bsd_syscalls_proto", + ], + visibility = ["//tools/group_crashes/backend/model:__pkg__"], +) + +cc_proto_library( + name = "session_cc_proto", + visibility = [ + "//fuzz/handlers:__pkg__", + ], + deps = [ + ":session_proto", + ], +) diff --git a/fuzz/proto/bsd.proto b/fuzz/proto/bsd.proto new file mode 100644 index 0000000..a2a2b38 --- /dev/null +++ b/fuzz/proto/bsd.proto @@ -0,0 +1,195 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +import "fuzz/proto/bsd_types.proto"; +import "fuzz/proto/bsd_enums.proto"; + +message ConnectX { + required FileDescriptor socket = 1; + required Endpoints endpoints = 2; + required SaeAssocID associd = 3; + repeated ConnectXFlag flags = 4; + // user_addr_t iov; + // unsigned int iovcnt; + // TODO(nedwill): support IOV + // repeated IOV connectx_iovs = 5; +} + +message DisconnectX { + required SaeAssocID associd = 1; + required int32 cid = 2; + required FileDescriptor fd = 3; +} + +message Endpoints { + required IfIdx sae_srcif = 1; + optional SockAddr sae_srcaddr = 2; + required SockAddr sae_dstaddr = 3; +} + +// message RecvmsgX { +// optional int32 s = 1; +// optional StructMsghdrx msgp = 2; +// optional uint32 cnt = 3; +// optional RecvmsgXFlags flags = 4; +// } + +// message SendmsgX { +// optional int32 s = 1; +// optional StructMsghdrx msgp = 2; +// optional uint32 cnt = 3; +// optional SendmsgXFlags flags = 4; +// } + +message NecpSessionAction { + optional FileDescriptor necp_fd = 1; + optional NecpSessionActionNumber action = 2; + optional bytes in_buffer = 3; + optional uint32 out_buffer_size = 4; +} + +// TODO(nedwill): support null client +enum NecpClientId { + CLIENT_0 = 0; + CLIENT_1 = 1; + CLIENT_2 = 2; +} + +message NecpClientAction { + optional FileDescriptor necp_fd = 1; + optional NecpClientId client_id = 3; + // TODO(nedwill): remove these fields + // optional NecpClientActionNumber action = 2; + // optional bytes buffer = 4; + + oneof action { + NecpClientActionAdd add = 5; + NecpClientActionRemove remove = 6; + NecpClientActionCopyParameters copy_parameters = 7; + // TODO(nedwill): implement remaining necp client commands + // NECP_CLIENT_ACTION_COPY_RESULT = 8; // Copy client result. Input: + // client_id; Output: result in buffer NECP_CLIENT_ACTION_COPY_LIST = 9; // + // Copy all client IDs. Output: struct necp_client_list in buffer + // TODO(nedwill): this isn't actually used? + // NecpClientActionRequestNexusInstance request_nexus_instance = 10; + NecpClientActionAgent agent = 11; + // NECP_CLIENT_ACTION_COPY_AGENT = 12; // Copy agent content. Input: agent + // UUID; Output: struct netagent NECP_CLIENT_ACTION_COPY_INTERFACE = 13; // + // Copy interface details. Input: ifindex cast to UUID; Output: struct + // necp_interface_details NECP_CLIENT_ACTION_SET_STATISTICS = 14; // + // Deprecated NECP_CLIENT_ACTION_COPY_ROUTE_STATISTICS = 15; // Get route + // statistics. Input: client_id; Output: struct necp_stat_counts + // NECP_CLIENT_ACTION_AGENT_USE = 16; // Return the use count and increment + // the use count. Input/Output: struct necp_agent_use_parameters + // NECP_CLIENT_ACTION_MAP_SYSCTLS = 17; // Get the read-only sysctls memory + // location. Output: mach_vm_address_t NECP_CLIENT_ACTION_UPDATE_CACHE = 18; + // // Update heuristics and cache NECP_CLIENT_ACTION_COPY_CLIENT_UPDATE = + // 19; // Fetch an updated client for push-mode observer. Output: Client id, + // struct necp_client_observer_update in buffer + // NECP_CLIENT_ACTION_COPY_UPDATED_RESULT = 20; // Copy client result only + // if changed. Input: client_id; Output: result in buffer + // TODO(nedwill): these aren't implemented? + // NECP_CLIENT_ACTION_ADD_FLOW = 21; // Add a flow. Input: client_id; + // Output: struct necp_client_add_flow NECP_CLIENT_ACTION_REMOVE_FLOW = 22; + // // Remove a flow. Input: flow_id, optional struct ifnet_stats_per_flow + } +} + +message NecpClientActionAdd { + optional bytes buffer = 1; +} + +message NecpClientActionRemove {} + +message NecpClientActionCopyParameters { + optional uint32 copyout_size = 1; +} + +message IoctlReal { + oneof ioctl { + In6_AliasReq_64 siocaifaddr_in6_64 = 1; + IfReqFlags siocsifflags = 3; + } + optional FileDescriptor fd = 2; +} + +message Packet { + oneof packet { + TcpPacket tcp_packet = 1; + Tcp6Packet tcp6_packet = 2; + Ip4Packet ip4_packet = 3; + Ip6Packet ip6_packet = 4; + // TODO(nedwill): more packet types + // Stf4Packet, + // Udp6Packet, + // Rip6Packet, + // Icmp6Packet, + // Dest6Packet, + // Route6Packet, + // Frag6Packet, + // Ah6Packet, + // Esp6Packet, + // Ipcomp6Packet, + // Encap6Packet, + // Gif4Packet, + // Gif6Packet, + // Udp4Packet, + // Rip4Packet, + // Icmp4Packet, + // Igmp4Packet, + // Gre4Packet, + // Ah4Packet, + // Esp4Packet, + // Ipcomp4Packet, + // Encap4Packet, + // Div4Packet, + + // TODO(nedwill): enable this once other types are finished + bytes raw_ip4 = 1000; + bytes raw_ip6 = 1001; + } + // TODO(nedwill): specify metadata here for layout of mbuf +} + +message Key { + required bytes mbuf_data = 1; +} + +message Ioctl { + required IoctlIdx ioctl_idx = 1; + required FileDescriptor fd = 2; +} + +message KEvent { + required uint32 kq = 1; + repeated KEventStruct changelist = 2; + repeated KEventStruct eventlist = 3; + // TODO: timeout +} diff --git a/fuzz/proto/bsd_enums.proto b/fuzz/proto/bsd_enums.proto new file mode 100644 index 0000000..0bed53f --- /dev/null +++ b/fuzz/proto/bsd_enums.proto @@ -0,0 +1,1303 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +// This file contains constants for BSD enums and flags. +// Enums for other purposes (like to restrict fuzzed file +// descriptors) should go elsewhere (bsd_types.proto). + +// If names conflict with internal ones, the convention +// is to prefix with FUZZED_. + +enum MmapProt { + PROT_READ = 1; + PROT_WRITE = 2; + PROT_EXEC = 4; +} + +enum MsyncFlags { + MS_ASYNC = 1; + MS_INVALIDATE = 2; + MS_SYNC = 16; +} + +enum MmapFlags { + MAP_FILE = 0; + MAP_SHARED = 1; + MAP_PRIVATE = 2; + MAP_FIXED = 16; + MAP_HASSEMAPHORE = 512; + MAP_NOCACHE = 1024; + MAP_JIT = 2048; + MAP_ANON = 4096; + MAP_32BIT = 32768; +} + +enum MadviseFlags { + MADV_NORMAL = 0; + MADV_RANDOM = 1; + MADV_SEQUENTIAL = 2; + MADV_WILLNEED = 3; + MADV_DONTNEED = 4; + MADV_FREE = 5; + MADV_ZERO_WIRED_PAGES = 6; +} + +enum MlockallFlags { + MCL_CURRENT = 1; + MCL_FUTURE = 2; +} + +enum ShmatFlags { + SHM_RDONLY = 4096; + SHM_RND = 8192; +} + +enum PollfdEvents { + option allow_alias = true; + POLLIN = 1; + POLLPRI = 2; + POLLOUT = 4; + POLLWRNORM = 4; + POLLERR = 8; + POLLHUP = 16; + POLLNVAL = 32; + POLLRDNORM = 64; + POLLRDBAND = 128; + POLLWRBAND = 256; +} + +enum FlockOp { + LOCK_SH = 1; + LOCK_EX = 2; + LOCK_NB = 4; + LOCK_UN = 8; +} + +enum AtFlags { + AT_EACCESS = 16; + AT_SYMLINK_NOFOLLOW = 32; + AT_SYMLINK_FOLLOW = 64; + AT_REMOVEDIR = 128; +} + +enum GetitimerWhich { + FUZZED_ITIMER_REAL = 0; + FUZZED_ITIMER_VIRTUAL = 1; + FUZZED_ITIMER_PROF = 2; +} + +enum WaitOptions { + FUZZED_WNOHANG = 1; + FUZZED_WUNTRACED = 2; + FUZZED_WEXITED = 4; + FUZZED_WSTOPPED = 8; + FUZZED_WCONTINUED = 16; + FUZZED_WNOWAIT = 32; +} + +enum FcntlDupfd { + F_DUPFD = 0; + F_DUPFD_CLOEXEC = 67; +} + +enum FcntlGetflags { + F_GETFD = 1; + F_GETFL = 3; +} + +enum FcntlLock { + F_GETLK = 7; + F_SETLK = 8; + F_SETLKW = 9; +} + +enum FcntlFlags { + FD_CLOEXEC = 1; +} + +enum FlockType { + F_RDLCK = 1; + F_UNLCK = 2; + F_WRLCK = 3; +} + +enum ChflagsFlags { + SF_ARCHIVED = 65536; + SF_IMMUTABLE = 131072; + SF_APPEND = 262144; + SF_NOUNLINK = 1048576; +} + +enum OpenFlags { + O_RDONLY = 0; + O_WRONLY = 1; + O_RDWR = 2; + O_NONBLOCK = 4; + O_APPEND = 8; + O_SHLOCK = 16; + O_EXLOCK = 32; + O_NOFOLLOW = 256; + O_CREAT = 512; + O_TRUNC = 1024; + O_EXCL = 2048; + O_EVTONLY = 32768; + O_SYMLINK = 2097152; + O_CLOEXEC = 16777216; + O_NOFOLLOW_ANY = 536870912; +} + +enum PermissionMode { + S_IXOTH = 1; + S_IWOTH = 2; + S_IROTH = 4; + S_IXGRP = 8; + S_IWGRP = 16; + S_IRGRP = 32; + S_IXUSR = 64; + S_IWUSR = 128; + S_IRUSR = 256; +} + +enum SeekWhence { + FUZZED_SEEK_SET = 0; + FUZZED_SEEK_CUR = 1; + FUZZED_SEEK_END = 2; + FUZZED_SEEK_HOLE = 3; + FUZZED_SEEK_DATA = 4; +} + +enum ConfValue { + FUZZED_PC_LINK_MAX = 1; + FUZZED_PC_MAX_CANON = 2; + FUZZED_PC_MAX_INPUT = 3; + FUZZED_PC_NAME_MAX = 4; + FUZZED_PC_PATH_MAX = 5; + FUZZED_PC_PIPE_BUF = 6; + FUZZED_PC_CHOWN_RESTRICTED = 7; + FUZZED_PC_NO_TRUNC = 8; + FUZZED_PC_VDISABLE = 9; + FUZZED_PC_XATTR_SIZE_BITS = 26; + FUZZED_PC_MIN_HOLE_SIZE = 27; +} + +// TODO(nwach): Can this comment be trusted? Merge with other types below. +// Currently has only one type supported in XNU +enum Ip6RtType { + IPV6_RTHDR_TYPE_0 = 0; +} + +enum Fflags { + option allow_alias = true; + NOTE_FFNOP = 0; + NOTE_DELETE = 1; + NOTE_LOWAT = 1; + NOTE_SECONDS = 1; + NOTE_TRACK = 1; + NOTE_USECONDS = 2; + NOTE_WRITE = 2; + NOTE_EXTEND = 4; + NOTE_NSECONDS = 4; + NOTE_ATTRIB = 8; + NOTE_LINK = 16; + NOTE_RENAME = 32; + NOTE_REVOKE = 64; + // TODO(nwach): These values are out of range of int32. + //NOTE_FFLAGSMASK = 16777215; + //NOTE_TRIGGER = 16777216; + //NOTE_EXEC = 536870912; + //NOTE_FFAND = 1073741824; + //NOTE_FORK = 1073741824; + //NOTE_EXIT = 2147483648; + //NOTE_FFOR = 2147483648; + //NOTE_FFCOPY = 3221225472; + //NOTE_FFCTRLMASK = 3221225472; +} + +enum SocketType { + SOCK_STREAM = 1; + SOCK_DGRAM = 2; + SOCK_RAW = 3; + SOCK_RDM = 4; + SOCK_SEQPACKET = 5; +} + +enum ShutdownHow { + SHUT_RD = 0; + SHUT_WR = 1; + SHUT_RDWR = 2; +} + +enum MsgFlags { + MSG_OOB = 1; + MSG_PEEK = 2; + MSG_DONTROUTE = 4; + MSG_EOR = 8; + MSG_WAITALL = 64; + MSG_DONTWAIT = 128; + MSG_EOF = 256; +} + +enum ConnectXFlag { + CONNECT_RESUME_ON_READ_WRITE = 1; + CONNECT_DATA_IDEMPOTENT = 2; + CONNECT_DATA_AUTHENTICATED = 4; +} + +enum NecpOpenFlag { + NECP_OPEN_FLAG_OBSERVER = 1; + NECP_OPEN_FLAG_BACKGROUND = 2; + NECP_OPEN_FLAG_PUSH_OBSERVER = 4; +} + +enum NecpClientActionNumber { + NECP_CLIENT_ACTION_ADD = 1; + NECP_CLIENT_ACTION_REMOVE = 2; + NECP_CLIENT_ACTION_COPY_PARAMETERS = 3; + NECP_CLIENT_ACTION_COPY_RESULT = 4; + NECP_CLIENT_ACTION_COPY_LIST = 5; + NECP_CLIENT_ACTION_REQUEST_NEXUS_INSTANCE = 6; + NECP_CLIENT_ACTION_AGENT = 7; + NECP_CLIENT_ACTION_COPY_AGENT = 8; + NECP_CLIENT_ACTION_COPY_INTERFACE = 9; + NECP_CLIENT_ACTION_SET_STATISTICS = 10; + NECP_CLIENT_ACTION_COPY_ROUTE_STATISTICS = 11; + NECP_CLIENT_ACTION_AGENT_USE = 12; + NECP_CLIENT_ACTION_MAP_SYSCTLS = 13; + NECP_CLIENT_ACTION_UPDATE_CACHE = 14; + NECP_CLIENT_ACTION_COPY_CLIENT_UPDATE = 15; + NECP_CLIENT_ACTION_COPY_UPDATED_RESULT = 16; + NECP_CLIENT_ACTION_ADD_FLOW = 17; + NECP_CLIENT_ACTION_REMOVE_FLOW = 18; +} + +enum NecpSessionActionNumber { + NECP_SESSION_ACTION_POLICY_ADD = 1; + NECP_SESSION_ACTION_POLICY_GET = 2; + NECP_SESSION_ACTION_POLICY_DELETE = 3; + NECP_SESSION_ACTION_POLICY_APPLY_ALL = 4; + NECP_SESSION_ACTION_POLICY_LIST_ALL = 5; + NECP_SESSION_ACTION_POLICY_DELETE_ALL = 6; + NECP_SESSION_ACTION_SET_SESSION_PRIORITY = 7; + NECP_SESSION_ACTION_LOCK_SESSION_TO_PROC = 8; + NECP_SESSION_ACTION_REGISTER_SERVICE = 9; + NECP_SESSION_ACTION_UNREGISTER_SERVICE = 10; + NECP_SESSION_ACTION_POLICY_DUMP_ALL = 11; +} + +enum IfruFlag { + IFF_UP = 1; + IFF_BROADCAST = 2; + IFF_DEBUG = 4; + IFF_LOOPBACK = 8; + IFF_POINTOPOINT = 16; + IFF_NOTRAILERS = 32; + IFF_RUNNING = 64; + IFF_NOARP = 128; + IFF_PROMISC = 256; + IFF_ALLMULTI = 512; + IFF_OACTIVE = 1024; + IFF_SIMPLEX = 2048; + IFF_LINK0 = 4096; + IFF_LINK1 = 8192; + IFF_LINK2 = 16384; + IFF_MULTICAST = 32768; +} + +enum IfraFlag { + IN6_IFF_ANYCAST = 1; // anycast address + IN6_IFF_TENTATIVE = 2; // tentative address + IN6_IFF_DUPLICATED = 4; // DAD detected duplicate + IN6_IFF_DETACHED = 8; // may be detached from the link + IN6_IFF_DEPRECATED = 16; // deprecated address + IN6_IFF_NODAD = 32; + IN6_IFF_AUTOCONF = 64; // autoconfigurable address. + IN6_IFF_TEMPORARY = 128; // temporary (anonymous) address. + IN6_IFF_DYNAMIC = 256; // assigned by DHCPv6 service + IN6_IFF_OPTIMISTIC = 512; // optimistic DAD, i.e. RFC 4429 + IN6_IFF_SECURED = 1024; // cryptographically generated + IN6_IFF_SWIFTDAD = 2048; // DAD with no delay + IN6_IFF_CLAT46 = 4096; // Address reserved for CLAT46 + IN6_IFF_NOPFX = 32768; // Depreciated. Don't use. +} + +enum Icmp6Type { + ICMP6_DST_UNREACH = 1; // dest unreachable, codes: + ICMP6_PACKET_TOO_BIG = 2; // packet too big + ICMP6_TIME_EXCEEDED = 3; // time exceeded, code: + ICMP6_PARAM_PROB = 4; // ip6 header bad + ICMP6_ECHO_REQUEST = 128; // echo service + ICMP6_ECHO_REPLY = 129; // echo reply + MLD_LISTENER_QUERY = 130; // multicast listener query + MLD_LISTENER_REPORT = 131; // multicast listener report + MLD_LISTENER_DONE = 132; // multicast listener done + ND_ROUTER_SOLICIT = 133; // router solicitation + ND_ROUTER_ADVERT = 134; // router advertisement + ND_NEIGHBOR_SOLICIT = 135; // neighbor solicitation + ND_NEIGHBOR_ADVERT = 136; // neighbor advertisement + ND_REDIRECT = 137; // redirect + ICMP6_ROUTER_RENUMBERING = 138; // router renumbering + ICMP6_WRUREQUEST = 139; // who are you request + ICMP6_WRUREPLY = 140; // who are you reply + MLDV2_LISTENER_REPORT = 143; // RFC3810 listener report + MLD_MTRACE_RESP = 200; // mtrace resp (to sender) + MLD_MTRACE = 201; // mtrace messages +} + +enum Icmp6Code { + ICMP6_DST_UNREACH_NOROUTE = 0; // no route to destination + ICMP6_DST_UNREACH_ADMIN = 1; // administratively prohibited + ICMP6_DST_UNREACH_NOTNEIGHBOR = 2; // not a neighbor(obsolete) + ICMP6_DST_UNREACH_ADDR = 3; // address unreachable + ICMP6_DST_UNREACH_NOPORT = 4; // port unreachable + ICMP6_INFOMSG_MASK = 128; // all informational messages + ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET = 255; // rr seq num reset +} + +enum TcpFlag { + TH_FIN = 1; + TH_SYN = 2; + TH_RST = 4; + TH_PUSH = 8; + TH_ACK = 16; + TH_URG = 32; + TH_ECE = 64; + TH_CWR = 128; +} + +// TODO(nedwill): review all commented (potentially nondeterministic) ioctls +// to ensure determinism on each loop with manual cleanup +enum IoctlIdx { + SIOCSHIWAT = 1; + SIOCGHIWAT = 2; + SIOCSLOWAT = 3; + SIOCGLOWAT = 4; + SIOCATMARK = 5; + SIOCSPGRP = 6; + SIOCGPGRP = 7; + SIOCSIFADDR = 8; + OSIOCGIFADDR = 9; + SIOCSIFDSTADDR = 10; + OSIOCGIFDSTADDR = 11; + SIOCSIFFLAGS = 12; + SIOCGIFFLAGS = 13; + OSIOCGIFBRDADDR = 14; + SIOCSIFBRDADDR = 15; + OSIOCGIFCONF = 16; + OSIOCGIFCONF32 = 17; + OSIOCGIFCONF64 = 18; + OSIOCGIFNETMASK = 19; + SIOCSIFNETMASK = 20; + SIOCGIFMETRIC = 21; + SIOCSIFMETRIC = 22; + SIOCDIFADDR = 23; + SIOCAIFADDR = 24; + SIOCGIFADDR = 25; + SIOCGIFDSTADDR = 26; + SIOCGIFBRDADDR = 27; + SIOCGIFCONF = 28; + SIOCGIFCONF32 = 29; + SIOCGIFCONF64 = 30; + SIOCGIFNETMASK = 31; + SIOCADDMULTI = 32; + SIOCDELMULTI = 33; + SIOCGIFMTU = 34; + SIOCSIFMTU = 35; + SIOCGIFPHYS = 36; + SIOCSIFPHYS = 37; + SIOCSIFMEDIA = 38; + SIOCGIFMEDIA = 39; + SIOCGIFMEDIA32 = 40; + SIOCGIFMEDIA64 = 41; + SIOCSIFGENERIC = 42; + SIOCGIFGENERIC = 43; + SIOCSIFLLADDR = 44; + SIOCGIFSTATUS = 45; + SIOCSIFPHYADDR = 46; + SIOCGIFPSRCADDR = 47; + SIOCGIFPDSTADDR = 48; + SIOCDIFPHYADDR = 49; + SIOCGIFDEVMTU = 50; + SIOCSIFALTMTU = 51; + SIOCPROTOATTACH = 52; + SIOCPROTODETACH = 53; + SIOCIFCREATE = 54; + SIOCIFDESTROY = 55; + SIOCSIFVLAN = 56; + SIOCGIFVLAN = 57; + SIOCSETVLAN = 58; + SIOCGETVLAN = 59; + SIOCSIFDEVMTU = 60; + SIOCIFGCLONERS = 61; + SIOCIFGCLONERS32 = 62; + SIOCIFGCLONERS64 = 63; + SIOCGIFASYNCMAP = 64; + SIOCSIFASYNCMAP = 65; + SIOCSIFKPI = 66; + SIOCGIFKPI = 67; + SIOCGIFWAKEFLAGS = 68; + SIOCGIFGETRTREFCNT = 69; + SIOCGIFLINKQUALITYMETRIC = 70; + SIOCSETROUTERMODE = 71; + SIOCGIFEFLAGS = 72; + SIOCSIFDESC = 73; + SIOCGIFDESC = 74; + SIOCSIFLINKPARAMS = 75; + SIOCGIFLINKPARAMS = 76; + SIOCGIFQUEUESTATS = 77; + SIOCSIFTHROTTLE = 78; + SIOCGIFTHROTTLE = 79; + SIOCGASSOCIDS = 80; + SIOCGCONNIDS = 81; + SIOCGCONNINFO = 82; + SIOCGASSOCIDS32 = 83; + SIOCGASSOCIDS64 = 84; + SIOCGCONNIDS32 = 85; + SIOCGCONNIDS64 = 86; + SIOCGCONNINFO32 = 87; + SIOCGCONNINFO64 = 88; + SIOCSCONNORDER = 89; + SIOCGCONNORDER = 90; + SIOCSIFLOG = 91; + SIOCGIFLOG = 92; + SIOCGIFDELEGATE = 93; + SIOCGIFLLADDR = 94; + SIOCGIFTYPE = 95; + SIOCGIFEXPENSIVE = 96; + SIOCSIFEXPENSIVE = 97; + SIOCGIF2KCL = 98; + SIOCSIF2KCL = 99; + SIOCGSTARTDELAY = 100; + SIOCAIFAGENTID = 101; + SIOCDIFAGENTID = 102; + SIOCGIFAGENTIDS = 103; + SIOCGIFAGENTDATA = 104; + SIOCGIFAGENTIDS32 = 105; + SIOCGIFAGENTIDS64 = 106; + SIOCGIFAGENTDATA32 = 107; + SIOCGIFAGENTDATA64 = 108; + SIOCSIFINTERFACESTATE = 109; + SIOCGIFINTERFACESTATE = 110; + SIOCSIFPROBECONNECTIVITY = 111; + SIOCGIFPROBECONNECTIVITY = 112; + SIOCGIFFUNCTIONALTYPE = 113; + SIOCSIFNETSIGNATURE = 114; + SIOCGIFNETSIGNATURE = 115; + SIOCGECNMODE = 116; + SIOCSECNMODE = 117; + SIOCSIFORDER = 118; + SIOCGIFORDER = 119; + SIOCSQOSMARKINGMODE = 120; + SIOCSFASTLANECAPABLE = 121; + SIOCSQOSMARKINGENABLED = 122; + SIOCSFASTLEENABLED = 123; + SIOCGQOSMARKINGMODE = 124; + SIOCGQOSMARKINGENABLED = 125; + SIOCSIFTIMESTAMPENABLE = 126; + SIOCSIFTIMESTAMPDISABLE = 127; + SIOCGIFTIMESTAMPENABLED = 128; + SIOCSIFDISABLEOUTPUT = 129; + SIOCGIFAGENTLIST = 130; + SIOCGIFAGENTLIST32 = 131; + SIOCGIFAGENTLIST64 = 132; + SIOCSIFLOWINTERNET = 133; + SIOCGIFLOWINTERNET = 134; + SIOCGIFNAT64PREFIX = 135; + SIOCSIFNAT64PREFIX = 136; + SIOCGIFNEXUS = 137; + SIOCSIFADDR_IN6 = 138; + SIOCGIFADDR_IN6 = 139; + SIOCSIFDSTADDR_IN6 = 140; + SIOCSIFNETMASK_IN6 = 141; + SIOCGIFDSTADDR_IN6 = 142; + SIOCGIFNETMASK_IN6 = 143; + SIOCDIFADDR_IN6 = 144; + SIOCAIFADDR_IN6 = 145; + SIOCAIFADDR_IN6_32 = 146; + SIOCAIFADDR_IN6_64 = 147; + SIOCSIFPHYADDR_IN6 = 148; + SIOCSIFPHYADDR_IN6_32 = 149; + SIOCSIFPHYADDR_IN6_64 = 150; + SIOCGIFPSRCADDR_IN6 = 151; + SIOCGIFPDSTADDR_IN6 = 152; + SIOCGIFAFLAG_IN6 = 153; + SIOCGDRLST_IN6 = 154; + SIOCGDRLST_IN6_32 = 155; + SIOCGDRLST_IN6_64 = 156; + SIOCGPRLST_IN6 = 157; + SIOCGPRLST_IN6_32 = 158; + SIOCGPRLST_IN6_64 = 159; + OSIOCGIFINFO_IN6 = 160; + SIOCGIFINFO_IN6 = 161; + SIOCSNDFLUSH_IN6 = 162; + SIOCGNBRINFO_IN6 = 163; + SIOCGNBRINFO_IN6_32 = 164; + SIOCGNBRINFO_IN6_64 = 165; + SIOCSPFXFLUSH_IN6 = 166; + SIOCSRTRFLUSH_IN6 = 167; + SIOCGIFALIFETIME_IN6 = 168; + SIOCSIFALIFETIME_IN6 = 169; + SIOCGIFSTAT_IN6 = 170; + SIOCGIFSTAT_ICMP6 = 171; + SIOCSDEFIFACE_IN6 = 172; + SIOCGDEFIFACE_IN6 = 173; + SIOCSDEFIFACE_IN6_32 = 174; + SIOCSDEFIFACE_IN6_64 = 175; + SIOCGDEFIFACE_IN6_32 = 176; + SIOCGDEFIFACE_IN6_64 = 177; + SIOCSIFINFO_FLAGS = 178; + SIOCSSCOPE6 = 179; + SIOCGSCOPE6 = 180; + SIOCGSCOPE6DEF = 181; + SIOCSIFPREFIX_IN6 = 182; + SIOCGIFPREFIX_IN6 = 183; + SIOCDIFPREFIX_IN6 = 184; + SIOCAIFPREFIX_IN6 = 185; + SIOCCIFPREFIX_IN6 = 186; + SIOCSGIFPREFIX_IN6 = 187; + SIOCAADDRCTL_POLICY = 188; + SIOCDADDRCTL_POLICY = 189; + SIOCPROTOATTACH_IN6 = 190; + SIOCPROTOATTACH_IN6_32 = 191; + SIOCPROTOATTACH_IN6_64 = 192; + SIOCPROTODETACH_IN6 = 193; + SIOCLL_START = 194; + SIOCLL_START_32 = 195; + SIOCLL_START_64 = 196; + SIOCLL_STOP = 197; + SIOCAUTOCONF_START = 198; + SIOCAUTOCONF_STOP = 199; + SIOCDRADD_IN6 = 200; + SIOCDRADD_IN6_32 = 201; + SIOCDRADD_IN6_64 = 202; + SIOCDRDEL_IN6 = 203; + SIOCDRDEL_IN6_32 = 204; + SIOCDRDEL_IN6_64 = 205; + SIOCSETROUTERMODE_IN6 = 206; + SIOCLL_CGASTART = 207; + SIOCLL_CGASTART_32 = 208; + SIOCLL_CGASTART_64 = 209; + SIOCGIFCGAPREP_IN6 = 210; + SIOCSIFCGAPREP_IN6 = 211; + DIOCSTART = 212; + DIOCSTOP = 213; + DIOCADDRULE = 214; + DIOCGETSTARTERS = 215; + DIOCGETRULES = 216; + DIOCGETRULE = 217; + DIOCSTARTREF = 218; + DIOCSTOPREF = 219; + DIOCCLRSTATES = 220; + DIOCGETSTATE = 221; + DIOCSETSTATUSIF = 222; + DIOCGETSTATUS = 223; + DIOCCLRSTATUS = 224; + DIOCNATLOOK = 225; + DIOCSETDEBUG = 226; + DIOCGETSTATES = 227; + DIOCCHANGERULE = 228; + DIOCINSERTRULE = 229; + DIOCDELETERULE = 230; + DIOCSETTIMEOUT = 231; + DIOCGETTIMEOUT = 232; + DIOCADDSTATE = 233; + DIOCCLRRULECTRS = 234; + DIOCGETLIMIT = 235; + DIOCSETLIMIT = 236; + DIOCKILLSTATES = 237; + DIOCSTARTALTQ = 238; + DIOCSTOPALTQ = 239; + DIOCADDALTQ = 240; + DIOCGETALTQS = 241; + DIOCGETALTQ = 242; + DIOCCHANGEALTQ = 243; + DIOCGETQSTATS = 244; + DIOCBEGINADDRS = 245; + DIOCADDADDR = 246; + DIOCGETADDRS = 247; + DIOCGETADDR = 248; + DIOCCHANGEADDR = 249; + DIOCGETRULESETS = 250; + DIOCGETRULESET = 251; + DIOCRCLRTABLES = 252; + DIOCRADDTABLES = 253; + DIOCRDELTABLES = 254; + DIOCRGETTABLES = 255; + DIOCRGETTSTATS = 256; + DIOCRCLRTSTATS = 257; + DIOCRCLRADDRS = 258; + DIOCRADDADDRS = 259; + DIOCRDELADDRS = 260; + DIOCRSETADDRS = 261; + DIOCRGETADDRS = 262; + DIOCRGETASTATS = 263; + DIOCRCLRASTATS = 264; + DIOCRTSTADDRS = 265; + DIOCRSETTFLAGS = 266; + DIOCRINADEFINE = 267; + DIOCOSFPFLUSH = 268; + DIOCOSFPADD = 269; + DIOCOSFPGET = 270; + DIOCXBEGIN = 271; + DIOCXCOMMIT = 272; + DIOCXROLLBACK = 273; + DIOCGETSRCNODES = 274; + DIOCCLRSRCNODES = 275; + DIOCSETHOSTID = 276; + DIOCIGETIFACES = 277; + DIOCSETIFFLAG = 278; + DIOCCLRIFFLAG = 279; + DIOCKILLSRCNODES = 280; + DIOCGIFSPEED = 281; +} + +// TODO(nedwill): clean these up, see if more headers have socket options +enum SocketOptName { + option allow_alias = true; + // socket.h + SO_DEBUG = 1; // turn on debugging info recording + SO_ACCEPTCONN = 2; // socket has had listen() + SO_REUSEADDR = 4; // allow local address reuse + SO_KEEPALIVE = 8; // keep connections alive + SO_DONTROUTE = 16; // just use interface addresses + SO_BROADCAST = 32; // permit sending of broadcast msgs + SO_USELOOPBACK = 64; // bypass hardware when possible + SO_LINGER = 128; // linger on close if data present (in ticks) + // SO_LINGER_SEC = 0x1080; // linger on close if data present (in seconds) + SO_OOBINLINE = 256; // leave received OOB data in line + SO_REUSEPORT = 512; // allow local address & port reuse + SO_TIMESTAMP = 1024; // timestamp received dgram traffic + SO_TIMESTAMP_MONOTONIC = + 2048; // Monotonically increasing timestamp on rcvd dgram + SO_ACCEPTFILTER = 4096; // there is an accept filter + SO_DONTTRUNC = 8192; // APPLE: Retain unread data + SO_WANTMORE = 16384; // APPLE: Give hint when more data ready + SO_WANTOOBFLAG = 32768; // APPLE: Want OOB in MSG_FLAG on receive + SO_NOWAKEFROMSLEEP = 65536; // Don't wake for traffic to this socket + SO_NOAPNFALLBK = 131072; // Don't attempt APN fallback for the socket + SO_TIMESTAMP_CONTINUOUS = + 262144; // Continuous monotonic timestamp on rcvd dgram + SO_SNDBUF = 4097; // send buffer size + SO_RCVBUF = 4098; // receive buffer size + SO_SNDLOWAT = 4099; // send low-water mark + SO_RCVLOWAT = 4100; // receive low-water mark + SO_SNDTIMEO = 4101; // send timeout + SO_RCVTIMEO = 4102; // receive timeout + SO_ERROR = 4103; // get error status and clear + SO_TYPE = 4104; // get socket type + SO_LABEL = 4112; // socket's MAC label + SO_PEERLABEL = 4113; // socket's peer MAC label + SO_NREAD = 4128; // APPLE: get 1st-packet byte count + SO_NKE = 4129; // APPLE: Install socket-level NKE + SO_NOSIGPIPE = 4130; // APPLE: No SIGPIPE on EPIPE + SO_NOADDRERR = + 4131; // APPLE: Returns EADDRNOTAVAIL when src is not available anymore + SO_NWRITE = + 4132; // APPLE: Get number of bytes currently in send socket buffer + SO_REUSESHAREUID = + 4133; // APPLE: Allow reuse of port/socket by different userids + SO_NOTIFYCONFLICT = 4134; // APPLE: send notification if there is a bind on a + // port which is already in use + SO_UPCALLCLOSEWAIT = 4135; // APPLE: block on close until an upcall returns + SO_LINGER_SEC = 4224; // linger on close if data present (in seconds) + SO_RESTRICTIONS = 4225; // APPLE: deny flag set + // SO_RESTRICT_DENY_IN = 0x1; /* deny inbound (trapdoor) */ + // SO_RESTRICT_DENY_OUT = 0x2; /* deny outbound (trapdoor) */ + // SO_RESTRICT_DENY_CELLULAR = 0x4; /* deny use of cellular (trapdoor) */ + // SO_RESTRICT_DENY_EXPENSIVE = 0x8; /* deny use of expensive if (trapdoor) + SO_RANDOMPORT = 4226; // APPLE: request local port randomization + SO_NP_EXTENSIONS = 4227; // To turn off some POSIX behavior + SO_EXECPATH = 4229; // Application Firewall Socket option + SO_TRAFFIC_CLASS = 4230; // Traffic service class (int) + SO_TC_BK_SYS = 100; // lowest class + SO_TC_BK = 200; + SO_TC_BE = 0; + SO_TC_RD = 300; + SO_TC_OAM = 400; + SO_TC_AV = 500; + SO_TC_RV = 600; + SO_TC_VI = 700; + SO_TC_VO = 800; + SO_TC_CTL = 900; // highest class + SO_TC_MAX = 10; // Total # of traffic classes + SO_TC_UNSPEC = -1; // Traffic class not specified + SO_RECV_TRAFFIC_CLASS = 4231; // Receive traffic class (bool) + SO_TRAFFIC_CLASS_DBG = 4232; // Debug traffic class (struct so_tcdbg) + SO_TRAFFIC_CLASS_STATS = 4233; // Traffic class statistics + SO_PRIVILEGED_TRAFFIC_CLASS = 4240; // Privileged traffic class (bool) + SO_DEFUNCTIT = 4241; // Defunct a socket (only in internal builds) + SO_DEFUNCTOK = 4352; // can be defunct'd + SO_ISDEFUNCT = 4353; // get defunct status + SO_OPPORTUNISTIC = 4354; // deprecated; use SO_TRAFFIC_CLASS + SO_FLUSH = 4355; // flush unsent data (int) + SO_RECV_ANYIF = 4356; // unrestricted inbound processing + SO_TRAFFIC_MGT_BACKGROUND = 4357; // Background traffic management + SO_FLOW_DIVERT_TOKEN = 4358; // flow divert token + SO_DELEGATED = 4359; // set socket as delegate (pid_t) + SO_DELEGATED_UUID = 4360; // set socket as delegate (uuid_t) + SO_NECP_ATTRIBUTES = 4361; // NECP socket attributes (domain, account, etc.) + SO_CFIL_SOCK_ID = 4368; // get content filter socket ID (cfil_sock_id_t) + SO_NECP_CLIENTUUID = 4369; // NECP Client uuid + SO_NUMRCVPKT = 4370; // number of datagrams in receive socket buffer + SO_AWDL_UNRESTRICTED = 4371; // try to use AWDL in restricted mode + SO_EXTENDED_BK_IDLE = + 4372; // extended time to keep socket idle after app is suspended (int) + SO_MARK_CELLFALLBACK = 4373; // Mark as initiated by cell fallback + SO_NET_SERVICE_TYPE = 4374; // Network service type + SO_QOSMARKING_POLICY_OVERRIDE = 4375; // int + SO_INTCOPROC_ALLOW = 4376; // Try to use internal co-processor interfaces. + SO_TC_NET_SERVICE_OFFSET = 10000; + SO_NETSVC_MARKING_LEVEL = 4377; // Get QoS marking in effect for socket + // tcp.h + TCPOPT_EOL = 0; + TCPOPT_NOP = 1; + TCPOPT_MAXSEG = 2; + TCPOLEN_MAXSEG = 4; + TCPOPT_WINDOW = 3; + TCPOLEN_WINDOW = 3; + TCPOPT_SACK_PERMITTED = 4; // Experimental + TCPOLEN_SACK_PERMITTED = 2; + TCPOPT_SACK = 5; // Experimental + TCPOLEN_SACK = 8; // len of sack block + TCPOPT_TIMESTAMP = 8; + TCPOLEN_TIMESTAMP = 10; + MAX_TCPOPTLEN = 40; // Absolute maximum TCP options len + TCPOPT_CC = 11; // CC options: RFC-1644 + TCPOPT_CCNEW = 12; + TCPOPT_CCECHO = 13; + TCPOLEN_CC = 6; + TCPOPT_SIGNATURE = 19; // Keyed MD5: RFC 2385 + TCPOLEN_SIGNATURE = 18; + TCPOPT_MULTIPATH = 30; + TCPOPT_FASTOPEN = 34; + TCPOLEN_FASTOPEN_REQ = 2; + MAX_SACK_BLKS = 6; // Max # SACK blocks stored at sender side + TCP_MAX_SACK = 4; // MAX # SACKs sent in any segment + TCP_MSS = 512; + TCP_MINMSS = 216; + TCP6_MSS = 1024; + TCP_MAXWIN = 65535; // largest value for (unscaled) window + TTCP_CLIENT_SND_WND = 4096; // dflt send window for T/TCP client + TCP_MAX_WINSHIFT = 14; // maximum window shift + TCP_NODELAY = 1; // don't delay send to coalesce packets + TCP_MAXSEG = 2; // set maximum segment size + TCP_NOPUSH = 4; // don't push last block of write + TCP_NOOPT = 8; // don't use TCP options + TCP_KEEPALIVE = 16; // idle time used when SO_KEEPALIVE is enabled + TCP_CONNECTIONTIMEOUT = 32; // connection timeout + PERSIST_TIMEOUT = 64; + TCP_RXT_CONNDROPTIME = 128; + TCP_RXT_FINDROP = 256; + TCP_KEEPINTVL = 257; // interval between keepalives + TCP_KEEPCNT = 258; // number of keepalives before close + TCP_SENDMOREACKS = 259; // always ack every other packet + TCP_ENABLE_ECN = 260; // Enable ECN on a connection + TCP_FASTOPEN = 261; // Enable/Disable TCP Fastopen on this socket + TCP_CONNECTION_INFO = 262; // State of TCP connection + TCP_INFO = 512; // retrieve tcp_info structure + TCP_MEASURE_SND_BW = 514; // Measure sender's bandwidth for this connection + TCP_NOTSENT_LOWAT = 513; // Low water mark for TCP unsent data + TCP_MEASURE_BW_BURST = 515; // Burst size to use for bandwidth measurement + TCP_PEER_PID = 516; // Lookup pid of the process we're connected to + TCP_ADAPTIVE_READ_TIMEOUT = 517; // Read timeout used as a multiple of RTT + TCP_ENABLE_MSGS = 518; + TCP_ADAPTIVE_WRITE_TIMEOUT = 519; // Write timeout used as a multiple of RTT + TCP_NOTIMEWAIT = 520; // Avoid going into time-wait + TCP_DISABLE_BLACKHOLE_DETECTION = 521; // disable PMTU blackhole detection + TCP_ECN_MODE = 528; // fine grain control for A/B testing + TCP_KEEPALIVE_OFFLOAD = 529; // offload keep alive processing to firmware + ECN_MODE_DEFAULT = 0; // per interface or system wide default + ECN_MODE_ENABLE = 1; // force enable ECN on connection + ECN_MODE_DISABLE = 2; // force disable ECN on connection + TCP_MAX_NOTIFY_ACK = 10; + TCP_NOTIFY_ACKNOWLEDGEMENT = 530; // Notify when data is acknowledged + MPTCP_SERVICE_TYPE = 531; // MPTCP Service type + TCP_FASTOPEN_FORCE_HEURISTICS = + 532; // Make sure TFO-heuristics never get disabled + MPTCP_SVCTYPE_HANDOVER = 0; // Default 0 + MPTCP_SVCTYPE_INTERACTIVE = 1; + MPTCP_SVCTYPE_AGGREGATE = 2; + MPTCP_SVCTYPE_MAX = 3; + TCP_RXT_MINIMUM_TIMEOUT = 533; + MPTCP_ALTERNATE_PORT = 534; + TCPI_OPT_TIMESTAMPS = 1; + TCPI_OPT_SACK = 2; + TCPI_OPT_WSCALE = 4; + TCPI_OPT_ECN = 8; + TCPI_FLAG_LOSSRECOVERY = 1; // Currently in loss recovery + TCPI_FLAG_STREAMING_ON = 2; // Streaming detection on + CONNINFO_MPTCP_VERSION = 3; + MPTCP_ITFSTATS_SIZE = 4; + MPTCPCI_FIRSTPARTY = 1; + TCPCI_OPT_TIMESTAMPS = 1; // Timestamps enabled + TCPCI_OPT_SACK = 2; // SACK enabled + TCPCI_OPT_WSCALE = 4; // Window scaling enabled + TCPCI_OPT_ECN = 8; // ECN enabled + TCPCI_FLAG_LOSSRECOVERY = 1; + TCPCI_FLAG_REORDERING_DETECTED = 2; + // in.h + IP_OPTIONS = 1; // buf/ip_opts; set/get IP options + IP_HDRINCL = 2; // int; header is included with data + IP_TOS = 3; // int; IP type of service and preced. + IP_TTL = 4; // int; IP time to live + IP_RECVOPTS = 5; // bool; receive all IP opts w/dgram + IP_RECVRETOPTS = 6; // bool; receive IP opts for response + IP_RECVDSTADDR = 7; // bool; receive IP dst addr w/dgram + IP_RETOPTS = 8; // ip_opts; set/get IP options + IP_MULTICAST_IF = 9; // u_char; set/get IP multicast i/f + IP_MULTICAST_TTL = 10; // u_char; set/get IP multicast ttl + IP_MULTICAST_LOOP = 11; // u_char; set/get IP multicast loopback + IP_ADD_MEMBERSHIP = 12; // ip_mreq; add an IP group membership + IP_DROP_MEMBERSHIP = 13; // ip_mreq; drop an IP group membership + IP_MULTICAST_VIF = 14; // set/get IP mcast virt. iface + IP_RSVP_ON = 15; // enable RSVP in kernel + IP_RSVP_OFF = 16; // disable RSVP in kernel + IP_RSVP_VIF_ON = 17; // set RSVP per-vif socket + IP_RSVP_VIF_OFF = 18; // unset RSVP per-vif socket + IP_PORTRANGE = 19; // int; range to choose for unspec port + IP_RECVIF = 20; // bool; receive reception if w/dgram + IP_IPSEC_POLICY = 21; // int; set/get security policy + IP_FAITH = 22; // deprecated + IP_STRIPHDR = 23; // bool: drop receive of raw IP header + IP_RECVTTL = 24; // bool; receive reception TTL w/dgram + IP_BOUND_IF = 25; // int; set/get bound interface + IP_PKTINFO = 26; // get pktinfo on recv socket, set src on sent dgram + IP_RECVTOS = 27; // bool; receive IP TOS w/dgram + IP_FW_ADD = 40; // add a firewall rule to chain + IP_FW_DEL = 41; // delete a firewall rule from chain + IP_FW_FLUSH = 42; // flush firewall rule chain + IP_FW_ZERO = 43; // clear single/all firewall counter(s) + IP_FW_GET = 44; // get entire firewall rule chain + IP_FW_RESETLOG = 45; // reset logging counters + IP_OLD_FW_ADD = 50; // add a firewall rule to chain + IP_OLD_FW_DEL = 51; // delete a firewall rule from chain + IP_OLD_FW_FLUSH = 52; // flush firewall rule chain + IP_OLD_FW_ZERO = 53; // clear single/all firewall counter(s) + IP_OLD_FW_GET = 54; // get entire firewall rule chain + IP_NAT__XXX = 55; // set/get NAT opts XXX Deprecated, do not use + IP_OLD_FW_RESETLOG = 56; // reset logging counters + IP_DUMMYNET_CONFIGURE = 60; // add/configure a dummynet pipe + IP_DUMMYNET_DEL = 61; // delete a dummynet pipe from chain + IP_DUMMYNET_FLUSH = 62; // flush dummynet + IP_DUMMYNET_GET = 64; // get entire dummynet pipes + IP_TRAFFIC_MGT_BACKGROUND = + 65; // int*; get background IO flags; set background IO + IP_MULTICAST_IFINDEX = 66; // int*; set/get IP multicast i/f index + IP_ADD_SOURCE_MEMBERSHIP = 70; // join a source-specific group + IP_DROP_SOURCE_MEMBERSHIP = 71; // drop a single source + IP_BLOCK_SOURCE = 72; // block a source + IP_UNBLOCK_SOURCE = 73; // unblock a source + IP_MSFILTER = 74; // set/get filter list + MCAST_JOIN_GROUP = 80; // join an any-source group + MCAST_LEAVE_GROUP = 81; // leave all sources for group + MCAST_JOIN_SOURCE_GROUP = 82; // join a source-specific group + MCAST_LEAVE_SOURCE_GROUP = 83; // leave a single source + MCAST_BLOCK_SOURCE = 84; // block a source + MCAST_UNBLOCK_SOURCE = 85; // unblock a source + IP_FORCE_OUT_IFP = 69; // not implemented; use IP_BOUND_IF instead + IP_NO_IFT_CELLULAR = 6969; // for internal use only + IP_OUT_IF = 9696; // for internal use only + IP_DEFAULT_MULTICAST_TTL = 1; // normally limit m'casts to 1 hop + IP_DEFAULT_MULTICAST_LOOP = 1; // normally hear sends if a member + IP_MIN_MEMBERSHIPS = 31; + IP_MAX_MEMBERSHIPS = 4095; + IP_MAX_GROUP_SRC_FILTER = 512; // sources per group + IP_MAX_SOCK_SRC_FILTER = 128; // sources per socket/group + IP_MAX_SOCK_MUTE_FILTER = 128; // XXX no longer used + MCAST_INCLUDE = 1; // fmode: include these source(s) + MCAST_EXCLUDE = 2; // fmode: exclude these source(s) + IP_PORTRANGE_DEFAULT = 0; // default range + IP_PORTRANGE_HIGH = 1; // "high" - request firewall bypass + IP_PORTRANGE_LOW = 2; // "low" - vouchsafe security + IPCTL_FORWARDING = 1; // act as router + IPCTL_SENDREDIRECTS = 2; // may send redirects when forwarding + IPCTL_DEFTTL = 3; // default TTL + IPCTL_DEFMTU = 4; // default MTU + IPCTL_RTEXPIRE = 5; // cloned route expiration time + IPCTL_RTMINEXPIRE = 6; // min value for expiration time + IPCTL_RTMAXCACHE = 7; // trigger level for dynamic expire + IPCTL_SOURCEROUTE = 8; // may perform source routes + IPCTL_DIRECTEDBROADCAST = 9; // may re-broadcast received packets + IPCTL_INTRQMAXLEN = 10; // max length of netisr queue + IPCTL_INTRQDROPS = 11; // number of netisr q drops + IPCTL_STATS = 12; // ipstat structure + IPCTL_ACCEPTSOURCEROUTE = 13; // may accept source routed packets + IPCTL_FASTFORWARDING = 14; // use fast IP forwarding code + IPCTL_KEEPFAITH = 15; // deprecated + IPCTL_GIF_TTL = 16; // default TTL for gif encap packet + IPCTL_MAXID = 17; + _DSCP_DF = 0; // RFC 2474 + _DSCP_CS0 = 0; // RFC 2474 + _DSCP_CS1 = 8; // RFC 2474 + _DSCP_CS2 = 16; // RFC 2474 + _DSCP_CS3 = 24; // RFC 2474 + _DSCP_CS4 = 32; // RFC 2474 + _DSCP_CS5 = 40; // RFC 2474 + _DSCP_CS6 = 48; // RFC 2474 + _DSCP_CS7 = 56; // RFC 2474 + _DSCP_EF = 46; // RFC 2474 + _DSCP_VA = 44; // RFC 5865 + _DSCP_AF11 = 10; // RFC 2597 + _DSCP_AF12 = 12; // RFC 2597 + _DSCP_AF13 = 14; // RFC 2597 + _DSCP_AF21 = 18; // RFC 2597 + _DSCP_AF22 = 20; // RFC 2597 + _DSCP_AF23 = 22; // RFC 2597 + _DSCP_AF31 = 26; // RFC 2597 + _DSCP_AF32 = 28; // RFC 2597 + _DSCP_AF33 = 30; // RFC 2597 + _DSCP_AF41 = 34; // RFC 2597 + _DSCP_AF42 = 36; // RFC 2597 + _DSCP_AF43 = 38; // RFC 2597 + _DSCP_52 = 52; // Wi-Fi WMM Certification: Sigma + _MAX_DSCP = 63; // coded on 6 bits + // in6.h + IPV6_OPTIONS = 1; // buf/ip6_opts; set/get IP6 options + IPV6_RECVOPTS = 5; // bool; receive all IP6 opts w/dgram + IPV6_RECVRETOPTS = 6; // bool; receive IP6 opts for response + IPV6_RECVDSTADDR = 7; // bool; receive IP6 dst addr w/dgram + IPV6_RETOPTS = 8; // ip6_opts; set/get IP6 options + IPV6_SOCKOPT_RESERVED1 = 3; // reserved for future use + IPV6_UNICAST_HOPS = 4; // int; IP6 hops + IPV6_MULTICAST_IF = 9; // __uint8_t; set/get IP6 multicast i/f + IPV6_MULTICAST_HOPS = 10; // __uint8_t; set/get IP6 multicast hops + IPV6_MULTICAST_LOOP = 11; // __uint8_t; set/get IP6 mcast loopback + IPV6_JOIN_GROUP = 12; // ip6_mreq; join a group membership + IPV6_LEAVE_GROUP = 13; // ip6_mreq; leave a group membership + IPV6_PORTRANGE = 14; // int; range to choose for unspec port + ICMP6_FILTER = 18; // icmp6_filter; icmp6 filter + IPV6_2292PKTINFO = 19; // bool; send/recv if, src/dst addr + IPV6_2292HOPLIMIT = 20; // bool; hop limit + IPV6_2292NEXTHOP = 21; // bool; next hop addr + IPV6_2292HOPOPTS = 22; // bool; hop-by-hop option + IPV6_2292DSTOPTS = 23; // bool; destinaion option + IPV6_2292RTHDR = 24; // ip6_rthdr: routing header + IPV6_2292PKTOPTIONS = 25; + IPV6_CHECKSUM = 26; // int; checksum offset for raw socket + IPV6_V6ONLY = 27; // bool; only bind INET6 at wildcard bind + IPV6_IPSEC_POLICY = 28; // struct; get/set security policy + IPV6_FAITH = 29; // deprecated + IPV6_FW_ADD = 30; // add a firewall rule to chain + IPV6_FW_DEL = 31; // delete a firewall rule from chain + IPV6_FW_FLUSH = 32; // flush firewall rule chain + IPV6_FW_ZERO = 33; // clear single/all firewall counter(s) + IPV6_FW_GET = 34; // get entire firewall rule chain + IPV6_RECVTCLASS = 35; // bool; recv traffic class values + IPV6_TCLASS = 36; // int; send traffic class value + IPV6_RTHDRDSTOPTS = 57; + IPV6_RECVPKTINFO = 61; + IPV6_RECVHOPLIMIT = 37; // bool; recv hop limit + IPV6_RECVRTHDR = 38; // bool; recv routing header + IPV6_RECVHOPOPTS = 39; // bool; recv hop-by-hop option + IPV6_RECVDSTOPTS = 40; // bool; recv dst option after rthdr + IPV6_RECVRTHDRDSTOPTS = 41; // bool; recv dst option before rthdr + IPV6_USE_MIN_MTU = 42; // bool; send packets at the minimum MTU + IPV6_RECVPATHMTU = 43; // bool; notify an according MTU + IPV6_PATHMTU = 44; + IPV6_REACHCONF = 45; + IPV6_3542PKTINFO = 46; // in6_pktinfo; send if, src addr + IPV6_3542HOPLIMIT = 47; // int; send hop limit + IPV6_3542NEXTHOP = 48; // sockaddr; next hop addr + IPV6_3542HOPOPTS = 49; // ip6_hbh; send hop-by-hop option + IPV6_3542DSTOPTS = 50; // ip6_dest; send dst option befor rthdr + IPV6_3542RTHDR = 51; // ip6_rthdr; send routing header + IPV6_AUTOFLOWLABEL = 59; // bool; attach flowlabel automagically + IPV6_DONTFRAG = 62; // bool; disable IPv6 fragmentation + IPV6_PREFER_TEMPADDR = 63; + IPV6_MSFILTER = 74; // struct __msfilterreq; + IPV6_BOUND_IF = 125; // int; set/get bound interface + IPV6_NO_IFT_CELLULAR = 6969; // for internal use only + IPV6_OUT_IF = 9696; // for internal use only + IPV6_RTHDR_LOOSE = 0; // this hop need not be a neighbor. + IPV6_RTHDR_STRICT = 1; // this hop must be a neighbor. + IPV6_RTHDR_TYPE_0_SOCKET = 0; // IPv6 routing header type 0 + IPV6_DEFAULT_MULTICAST_HOPS = 1; // normally limit m'casts to 1 hop + IPV6_DEFAULT_MULTICAST_LOOP = 1; // normally hear sends if a member + IPV6_MIN_MEMBERSHIPS = 31; + IPV6_MAX_MEMBERSHIPS = 4095; + IPV6_MAX_GROUP_SRC_FILTER = 512; // sources per group + IPV6_MAX_SOCK_SRC_FILTER = 128; // sources per socket/group + IPV6_PORTRANGE_DEFAULT = 0; // default range + IPV6_PORTRANGE_HIGH = 1; // "high" - request firewall bypass + IPV6_PORTRANGE_LOW = 2; // "low" - vouchsafe security + IPV6CTL_FORWARDING = 1; // act as router + IPV6CTL_SENDREDIRECTS = 2; // may send redirects when forwarding + IPV6CTL_DEFHLIM = 3; // default Hop-Limit + IPV6CTL_DEFMTU = 4; // default MTU + IPV6CTL_FORWSRCRT = 5; // forward source-routed dgrams + IPV6CTL_STATS = 6; // stats + IPV6CTL_MRTSTATS = 7; // multicast forwarding stats + IPV6CTL_MRTPROTO = 8; // multicast routing protocol + IPV6CTL_MAXFRAGPACKETS = 9; // max packets reassembly queue + IPV6CTL_SOURCECHECK = 10; // verify source route and intf + IPV6CTL_SOURCECHECK_LOGINT = 11; // minimume logging interval + IPV6CTL_ACCEPT_RTADV = 12; + IPV6CTL_KEEPFAITH = 13; // deprecated + IPV6CTL_LOG_INTERVAL = 14; + IPV6CTL_HDRNESTLIMIT = 15; + IPV6CTL_DAD_COUNT = 16; + IPV6CTL_AUTO_FLOWLABEL = 17; + IPV6CTL_DEFMCASTHLIM = 18; + IPV6CTL_GIF_HLIM = 19; // default HLIM for gif encap packet + IPV6CTL_KAME_VERSION = 20; + IPV6CTL_USE_DEPRECATED = 21; // use deprec addr (RFC2462 5.5.4) + IPV6CTL_RR_PRUNE = 22; // walk timer for router renumbering + IPV6CTL_MAPPED_ADDR = 23; + IPV6CTL_V6ONLY = 24; + IPV6CTL_RTEXPIRE = 25; // cloned route expiration time + IPV6CTL_RTMINEXPIRE = 26; // min value for expiration time + IPV6CTL_RTMAXCACHE = 27; // trigger level for dynamic expire + IPV6CTL_USETEMPADDR = 32; // use temporary addresses [RFC 4941] + IPV6CTL_TEMPPLTIME = 33; // preferred lifetime for tmpaddrs + IPV6CTL_TEMPVLTIME = 34; // valid lifetime for tmpaddrs + IPV6CTL_AUTO_LINKLOCAL = 35; // automatic link-local addr assign + IPV6CTL_RIP6STATS = 36; // raw_ip6 stats + IPV6CTL_PREFER_TEMPADDR = 37; // prefer temporary addr as src + IPV6CTL_ADDRCTLPOLICY = 38; // get/set address selection policy + IPV6CTL_USE_DEFAULTZONE = 39; // use default scope zone + IPV6CTL_MAXFRAGS = 41; // max fragments + IPV6CTL_MCAST_PMTU = 44; // enable pMTU discovery for mcast? + IPV6CTL_NEIGHBORGCTHRESH = 46; + IPV6CTL_MAXIFPREFIXES = 47; + IPV6CTL_MAXIFDEFROUTERS = 48; + IPV6CTL_MAXDYNROUTES = 49; + ICMPV6CTL_ND6_ONLINKNSRFC4861 = 50; + IPV6CTL_MAXID = 51; +} + +enum Domain { + AF_UNSPEC = 0; + AF_UNIX = 1; + AF_INET = 2; + AF_IMPLINK = 3; + AF_PUP = 4; + AF_CHAOS = 5; + AF_NS = 6; + AF_ISO = 7; + AF_ECMA = 8; + AF_DATAKIT = 9; + AF_CCITT = 10; + AF_SNA = 11; + AF_DECnet = 12; + AF_DLI = 13; + AF_LAT = 14; + AF_HYLINK = 15; + AF_APPLETALK = 16; + AF_ROUTE = 17; + AF_LINK = 18; + pseudo_AF_XTP = 19; + AF_COIP = 20; + AF_CNT = 21; + pseudo_AF_RTIP = 22; + AF_IPX = 23; + AF_SIP = 24; + pseudo_AF_PIP = 25; + AF_NDRV = 27; + AF_ISDN = 28; + pseudo_AF_KEY = 29; + AF_INET6 = 30; + AF_NATM = 31; + AF_SYSTEM = 32; + AF_NETBIOS = 33; + AF_PPP = 34; + pseudo_AF_HDRCMPLT = 35; + AF_RESERVED_36 = 36; + AF_IEEE80211 = 37; + AF_UTUN = 38; + AF_MULTIPATH = 39; + AF_MAX = 40; +} + +enum Protocol { + IPPROTO_IP = 0; + IPPROTO_ICMP = 1; + IPPROTO_IGMP = 2; + IPPROTO_GGP = 3; + IPPROTO_IPV4 = 4; + IPPROTO_TCP = 6; + IPPROTO_ST = 7; + IPPROTO_EGP = 8; + IPPROTO_PIGP = 9; + IPPROTO_RCCMON = 10; + IPPROTO_NVPII = 11; + IPPROTO_PUP = 12; + IPPROTO_ARGUS = 13; + IPPROTO_EMCON = 14; + IPPROTO_XNET = 15; + IPPROTO_CHAOS = 16; + IPPROTO_UDP = 17; + IPPROTO_MUX = 18; + IPPROTO_MEAS = 19; + IPPROTO_HMP = 20; + IPPROTO_PRM = 21; + IPPROTO_IDP = 22; + IPPROTO_TRUNK1 = 23; + IPPROTO_TRUNK2 = 24; + IPPROTO_LEAF1 = 25; + IPPROTO_LEAF2 = 26; + IPPROTO_RDP = 27; + IPPROTO_IRTP = 28; + IPPROTO_TP = 29; + IPPROTO_BLT = 30; + IPPROTO_NSP = 31; + IPPROTO_INP = 32; + IPPROTO_SEP = 33; + IPPROTO_3PC = 34; + IPPROTO_IDPR = 35; + IPPROTO_XTP = 36; + IPPROTO_DDP = 37; + IPPROTO_CMTP = 38; + IPPROTO_TPXX = 39; + IPPROTO_IL = 40; + IPPROTO_IPV6 = 41; + IPPROTO_SDRP = 42; + IPPROTO_ROUTIN = 43; + IPPROTO_FRAGMEN = 44; + IPPROTO_IDRP = 45; + IPPROTO_RSVP = 46; + IPPROTO_GRE = 47; + IPPROTO_MHRP = 48; + IPPROTO_BHA = 49; + IPPROTO_ESP = 50; + IPPROTO_AH = 51; + IPPROTO_INLSP = 52; + IPPROTO_SWIPE = 53; + IPPROTO_NHRP = 54; + IPPROTO_ICMPV6 = 58; + IPPROTO_NONE = 59; + IPPROTO_DSTOPTS = 60; + IPPROTO_AHIP = 61; + IPPROTO_CFTP = 62; + IPPROTO_HELLO = 63; + IPPROTO_SATEXPA = 64; + IPPROTO_KRYPTOLA = 65; + IPPROTO_RVD = 66; + IPPROTO_IPPC = 67; + IPPROTO_ADFS = 68; + IPPROTO_SATMON = 69; + IPPROTO_VISA = 70; + IPPROTO_IPCV = 71; + IPPROTO_CPNX = 72; + IPPROTO_CPHB = 73; + IPPROTO_WSN = 74; + IPPROTO_PVP = 75; + IPPROTO_BRSATMO = 76; + IPPROTO_ND = 77; + IPPROTO_WBMON = 78; + IPPROTO_WBEXPAK = 79; + IPPROTO_EON = 80; + IPPROTO_VMTP = 81; + IPPROTO_SVMTP = 82; + IPPROTO_VINES = 83; + IPPROTO_TTP = 84; + IPPROTO_IGP = 85; + IPPROTO_DGP = 86; + IPPROTO_TCF = 87; + IPPROTO_IGRP = 88; + IPPROTO_OSPFIGP = 89; + IPPROTO_SRPC = 90; + IPPROTO_LARP = 91; + IPPROTO_MTP = 92; + IPPROTO_AX25 = 93; + IPPROTO_IPEIP = 94; + IPPROTO_MICP = 95; + IPPROTO_SCCSP = 96; + IPPROTO_ETHERIP = 97; + IPPROTO_ENCAP = 98; + IPPROTO_APES = 99; + IPPROTO_GMTP = 100; + IPPROTO_PIM = 103; + IPPROTO_IPCOMP = 108; + IPPROTO_PGM = 113; + IPPROTO_SCTP = 132; + IPPROTO_DIVERT = 254; + IPPROTO_RAW = 255; + IPPROTO_MAX = 256; + IPPROTO_DONE = 257; + SOL_SOCKET = 65535; // options for socket level +} + +enum KEventFlag { + EV_ADD = 0x0001; + EV_DELETE = 0x0002; /* delete event from kq */ + EV_ENABLE = 0x0004; /* enable event */ + EV_DISABLE = 0x0008; /* disable event (not reported) */ + EV_ONESHOT = 0x0010; /* only report one occurrence */ + EV_CLEAR = 0x0020; /* clear event state after reporting */ + EV_RECEIPT = 0x0040; /* force immediate event output */ + EV_DISPATCH = 0x0080; /* disable event after reporting */ + EV_UDATA_SPECIFIC = 0x0100; /* unique kevent per udata value */ + EV_VANISHED = 0x0200; /* report that source has vanished */ + EV_SYSFLAGS = 0xF000; /* reserved by system */ + EV_FLAG0 = 0x1000; /* filter-specific flag */ + EV_FLAG1 = 0x2000; /* filter-specific flag */ +} + +enum KEventFilter { + EVFILT_READ = -1; + EVFILT_WRITE = -2; + EVFILT_AIO = -3; /* attached to aio requests */ + EVFILT_VNODE = -4; /* attached to vnodes */ + EVFILT_PROC = -5; /* attached to struct proc */ + EVFILT_SIGNAL = -6; /* attached to struct proc */ + EVFILT_TIMER = -7; /* timers */ + EVFILT_MACHPORT = -8; /* Mach portsets */ + EVFILT_FS = -9; /* Filesystem events */ + EVFILT_USER = -10; /* User events */ + EVFILT_VM = -12; /* Virtual memory events */ + EVFILT_EXCEPT = -15; /* Exception events */ + EVFILT_SOCK = -13; /* Socket events */ + EVFILT_MEMORYSTATUS = -14; /* Memorystatus events */ + EVFILT_NW_CHANNEL = -16; /* Skywalk channel events */ + EVFILT_WORKLOOP = -17; /* Workloop events */ + EVFILT_EXCLAVES_NOTIFICATION = -18; /* Exclave notification */ +} + +enum IdType { + FUZZED_P_ALL = 0; + FUZZED_P_PID = 1; + FUZZED_P_PGID = 2; +} + +enum RlimitWhich { + FUZZED_RLIMIT_CPU = 0; + FUZZED_RLIMIT_FSIZE = 1; + FUZZED_RLIMIT_DATA = 2; + FUZZED_RLIMIT_STACK = 3; + FUZZED_RLIMIT_CORE = 4; + FUZZED_RLIMIT_AS = 5; + FUZZED_RLIMIT_MEMLOCK = 6; + FUZZED_RLIMIT_NPROC = 7; + FUZZED_RLIMIT_NOFILE = 8; +} + +enum RUsageWho { + RUSAGE_SELF = 0; + RUSAGE_CHILDREN = -1; +} diff --git a/fuzz/proto/bsd_types.proto b/fuzz/proto/bsd_types.proto new file mode 100644 index 0000000..4c5a6f6 --- /dev/null +++ b/fuzz/proto/bsd_types.proto @@ -0,0 +1,340 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +import "fuzz/proto/bsd_enums.proto"; + +enum SaeAssocID { + ASSOCID_CASE_0 = 0; + ASSOCID_CASE_1 = 1; + ASSOCID_CASE_2 = 2; + ASSOCID_CASE_3 = 3; + // Note: negatives are inefficient in enum, perhaps we should + // just subtract 1 from the enum value as a hack. + ASSOCID_CASE_END = -1; +} + +message IOV { + required bytes iov_data = 1; +} + +message SockAddr { + oneof sockaddr { + SockAddrGeneric sockaddr_generic = 1; + // Legit IPv4 sockaddr + SockAddr4 sockaddr4 = 2; + // Legit IPv6 sockaddr + SockAddr6 sockaddr6 = 3; + // TODO(nedwill): Legit UNIX socket + // SockAddrUn sockaddrun = 3; + + // TODO(nedwill): sockaddr_ctl, sockaddr_* + + // Fuzz raw parsing of sockaddrs + // bytes raw_bytes = 1000; + } +} + +message SockAddrGeneric { + required Domain sa_family = 1; + required bytes sa_data = 2; +} + +message SockAddr4 { + required Domain sin_family = 1; + required PortNet sin_port = 2; + required InAddr sin_addr = 3; +} + +message SockAddr6 { + required Domain family = 1; + required PortNet port = 2; + required FlowInfo flow_info = 3; + required In6Addr sin6_addr = 4; + required ScopeId sin6_scope_id = 5; +} + +enum In6Addr { + IN6_ADDR_ANY = 0; + IN6_ADDR_LOOPBACK = 1; + IN6_ADDR_REAL = 2; + IN6_ADDR_SELF = 3; + IN6_ADDR_LINK_LOCAL = 4; + IN6_ADDR_UNSPECIFIED = 5; + IN6_ADDR_V4COMPAT = 7; + IN6_ADDR_V4MAPPED = 8; + IN6_ADDR_6TO4 = 9; + IN6_ADDR_LINKLOCAL = 10; + IN6_ADDR_SITELOCAL = 11; + IN6_ADDR_MULTICAST = 12; + IN6_ADDR_UNIQUE_LOCAL = 13; + IN6_ADDR_MC_NODELOCAL = 14; + IN6_ADDR_MC_INTFACELOCAL = 15; + IN6_ADDR_MC_LINKLOCAL = 16; + IN6_ADDR_MC_SITELOCAL = 17; + IN6_ADDR_MC_ORGLOCAL = 18; + IN6_ADDR_MC_GLOBAL = 19; + IN6_ADDR_LOCAL_ADDRESS = 20; + MAYBE_LOCALHOST = 16777216; +} + +// These can be used as-is for 32-bit ints. +enum InAddr { + IN4_ADDR_0 = 0; + IN4_ADDR_1 = 1; + IN4_ADDR_2 = 2; + // 24.130.58.208 + IN4_ADDR_4 = 411187920; + // 127.0.0.1 + IN4_ADDR_5 = 2130706433; + // 192.168.86.88 + IN4_ADDR_6 = -1062709672; + IN4_ADDR_BROADCAST = -1; +} + +enum FlowInfo { + FLOW_INFO_0 = 0; + FLOW_INFO_1 = 1; + FLOW_INFO_2 = 2; + FLOW_INFO_3 = 3; +} + +enum ScopeId { + SCOPE_ID_0 = 0; + SCOPE_ID_1 = 1; + SCOPE_ID_2 = 2; + SCOPE_ID_3 = 3; + SCOPE_ID_50 = 50; +} + +enum PortNet { + PORT_0 = 0; + PORT_1 = 1; + PORT_2 = 2; + PORT_5555 = 5555; + PORT_65000 = 65000; +} + +// Interface index obtained from if_nametoindex +// TODO(nedwill): do something more legit here +enum IfIdx { + IFIDX_CASE_0 = 0; + IFIDX_CASE_1 = 1; + IFIDX_CASE_2 = 2; + IFIDX_CASE_3 = 3; +} + +message NecpClientActionAgent { + repeated NecpTlv necp_tlv = 1; +} + +message NecpTlv { + // TODO(nedwill): make this an enum + optional uint32 necp_type = 1; + optional bytes data = 2; +} + +enum IfrName { + LO0 = 0; + STF0 = 1; + IPSEC0 = 2; + UTUN0 = 3; +} + +// ifreq.ifr_flags +message IfReqFlags { + required IfrName ifr_name = 1; + // #define ifr_flags ifr_ifru.ifru_flags /* flags */ + repeated IfruFlag flags = 2; +} + +message In6_AliasReq_64 { + required bytes ifra_name = 1; + required SockAddr6 ifra_addr = 2; + required SockAddr6 ifra_dstaddr = 3; + required SockAddr6 ifra_prefixmask = 4; + repeated IfraFlag ifra_flags = 5; + required In6AddrLifetime_64 ifra_lifetime = 6; +} + +message In6AddrLifetime_64 { + required uint64 ia6t_expire = 1; + required uint64 ia6t_preferred = 2; + required uint32 ia6t_vltime = 3; + required uint32 ia6t_pltime = 4; +} + +message TcpPacket { + required IpHdr ip_hdr = 1; + required TcpHdr tcp_hdr = 2; + optional bytes data = 3; +} + +message Tcp6Packet { + required Ip6Hdr ip6_hdr = 1; + required TcpHdr tcp_hdr = 2; + optional bytes data = 3; +} + +message Ip4Packet { + optional IpHdr ip_hdr = 1; + optional bytes data = 2; +} + +message Ip6Packet { + optional Ip6Hdr ip6_hdr = 1; + optional bytes data = 2; +} + +message Ip6Ext { + required Protocol ip6e_nxt = 1; + required uint32 ip6e_len = 2; +} + +message Ip6RtHdr { + required Protocol ip6r_nxt = 1; + required uint32 ip6r_len = 2; + required Ip6RtType ip6r_type = 3; + required uint32 ip6r_segleft = 4; +} + +message Ip6Rt0Hdr { + required Protocol ip6r0_nxt = 1; + required uint32 ip6r0_len = 2; + required uint32 ip6r0_type = 3; + required uint32 ip6r0_segleft = 4; + required uint32 ip6r0_reserved = 5; + required uint32 ip6r0_slmap = 6; + repeated In6Addr ip6r0_addr = 7; +} + +message Ip6FragHdr { + required Protocol ip6f_nxt = 1; + required uint32 ip6f_reserved = 2; + required uint32 ip6f_offlg = 3; + required uint32 ip6f_ident = 4; +} + +message Icmp6Hdr { + optional Icmp6Type icmp6_type = 1; + optional Icmp6Code icmp6_code = 2; + // optional uint32 icmp6_cksum = 3; + optional uint32 icmp6_dataun = 4; +} + +enum TcpSeq { + SEQ_1 = 1; + SEQ_2 = 2; + SEQ_3 = 3; + SEQ_4 = 4; + SEQ_5 = 5; + SEQ_6 = 6; + SEQ_7 = 7; + SEQ_8 = 8; +} + +message TcpHdr { + required PortNet th_sport = 1; + required PortNet th_dport = 2; + required TcpSeq th_seq = 3; + required TcpSeq th_ack = 4; + required uint32 th_off = 5; + repeated TcpFlag th_flags = 6; + required uint32 th_win = 7; + required uint32 th_sum = 8; + required uint32 th_urp = 9; + // Ned's extensions + required bool is_pure_syn = 10; + required bool is_pure_ack = 11; +} + +enum IpVersion { + IPV4 = 4; + IPV6 = 6; +} + +// TODO(nedwill): ip options +message IpHdr { + required uint32 ip_hl = 1; + required IpVersion ip_v = 2; + required uint32 ip_tos = 3; + required uint32 ip_len = 4; + required uint32 ip_id = 5; + required uint32 ip_off = 6; + required uint32 ip_ttl = 7; + required Protocol ip_p = 8; + required InAddr ip_src = 9; + required InAddr ip_dst = 10; +} + +message Ip6Hdr { + required Ip6Hdrctl ip6_hdrctl = 1; + required In6Addr ip6_src = 2; + required In6Addr ip6_dst = 3; +} + +message Ip6Hdrctl { + required uint32 ip6_un1_flow = 1; + required uint32 ip6_un1_plen = 2; + required Protocol ip6_un1_nxt = 3; + required uint32 ip6_un1_hlim = 4; +} + +// Some pre-allocated file descriptors to make guessing +// a real file descriptor more likely. +// TODO(nedwill): do experiments to determine how many of +// these to use. +enum FileDescriptor { + FD_0 = 0; + FD_1 = 1; + FD_2 = 2; + FD_3 = 3; + FD_4 = 4; + FD_5 = 5; + FD_6 = 6; + FD_7 = 7; + FD_8 = 8; + FD_9 = 9; +} + +message KEventStruct { + // TODO: ident is usually a file descriptor, but could be anything... + required FileDescriptor ident = 1; + required KEventFilter filter = 2; + repeated KEventFlag flags = 3; + // TODO: filter flags + required int64 fflags = 4; + required int64 data = 5; + required int64 udata = 6; +} + +message RLimit { + optional uint64 current = 1; + optional uint64 max = 2; +} diff --git a/fuzz/proto/common.proto b/fuzz/proto/common.proto new file mode 100644 index 0000000..6be1bd0 --- /dev/null +++ b/fuzz/proto/common.proto @@ -0,0 +1,45 @@ +// Copyright 2024 Google LLC +// +// 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. + +syntax = "proto2"; + +message ArbitrarySyscall { + optional uint32 number = 1; + optional bytes arguments = 2; +} + +enum PortNumberMach { + CASE_0 = 0; + CASE_1 = 1; + CASE_2 = 2; + CASE_3 = 3; + CASE_4 = 4; + CASE_5 = 5; + CASE_6 = 6; + CASE_7 = 7; + CASE_8 = 8; + CASE_9 = 9; + CASE_10 = 10; + CASE_11 = 11; + CASE_12 = 12; + CASE_13 = 13; + CASE_14 = 14; + CASE_15 = 15; + CASE_16 = 16; + CASE_17 = 17; + CASE_18 = 18; + CASE_19 = 19; + CASE_20 = 20; + CASE_NEGATIVE = -1; +} diff --git a/fuzz/proto/mach_message.proto b/fuzz/proto/mach_message.proto new file mode 100644 index 0000000..7088bac --- /dev/null +++ b/fuzz/proto/mach_message.proto @@ -0,0 +1,290 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +import "fuzz/proto/common.proto"; + +message MachPortName { + // TODO(nedwill): name vs. port? + optional MachPort port = 1; +} + +message MachPort { + optional PortNumberMach port = 1; +} + +message MachMsgHeaderBits { + // MACH_MSGH_BITS_SET + optional MachMsgTypeName remote = 1; + optional MachMsgTypeName local = 2; + optional MachMsgTypeName voucher = 3; + // optional uint32 other = 4; +} + +enum MachMsgCopyOptions { + FUZZED_MACH_MSG_PHYSICAL_COPY = 0; + FUZZED_MACH_MSG_VIRTUAL_COPY = 1; + FUZZED_MACH_MSG_ALLOCATE = 2; +} + +message MachMsgHeader { + optional MachMsgHeaderBits msgh_bits = 1; + // mach_msg_size_t msgh_size; + optional MachPort msgh_remote_port = 2; + optional MachPort msgh_local_port = 3; + optional MachPortName msgh_voucher_port = 4; + // TODO(nedwill): use enum for message ID or set it based on the body type + optional int32 msgh_id = 5; +} + +message MachMsgPortDescriptor { + optional MachPort name = 1; + optional MachMsgTypeName disposition = 2; + // optional MachMsgDescriptorType type = 3; +} + +// TODO(nedwill): pass data to this message +message MachMsgOolDescriptor { + // TODO(nedwill): nested data located at first element? + // optional uint64 address = 1; + optional bool deallocate = 2; + optional MachMsgCopyOptions copy = 3; + // TODO(nedwill): does it make sense to specify this here? + optional uint32 size = 4; +} + +message MachMsgOolPortsDescriptor { + // optional uint64 address = 1; + optional bool deallocate = 2; + optional MachMsgCopyOptions copy = 3; + optional MachMsgTypeName disposition = 4; + // optional MachMsgDescriptorType type = 5; + repeated MachPortName names = 5; +} + +enum MachMsgGuardFlags { + MACH_MSG_GUARD_FLAGS_NONE = 0x0000; + MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE = + 0x0001; /* Move the receive right and mark it as immovable */ + MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND = + 0x0002; /* Verify that the port is unguarded */ + MACH_MSG_GUARD_FLAGS_MASK = 0x0003; /* Valid flag bits */ +} + +message MachMsgGuardedPortDescriptor { + optional uint64 context = 1; + // mach_msg_guard_flags_t flags : 16; + optional MachMsgGuardFlags flags = 2; + optional MachMsgTypeName disposition = 3; + optional MachPortName name = 4; +} + +message MachMsgDescriptor { + oneof descriptor { + MachMsgPortDescriptor port = 1; + MachMsgOolDescriptor out_of_line = 2; + MachMsgOolPortsDescriptor ool_ports = 3; + MachMsgGuardedPortDescriptor guarded_port = 4; + MachMsgOolDescriptor out_of_line_volatile = 5; + } +} + +enum MachMsgOverwriteOption { + FUZZED_MACH_SEND_MSG = 0x00000001; + FUZZED_MACH_RCV_MSG = 0x00000002; + + FUZZED_MACH_RCV_LARGE = 0x00000004; /* report large message sizes */ + FUZZED_MACH_RCV_LARGE_IDENTITY = + 0x00000008; /* identify source of large messages */ + + FUZZED_MACH_SEND_TIMEOUT = 0x00000010; /* timeout value applies to send */ + FUZZED_MACH_SEND_OVERRIDE = 0x00000020; /* priority override for send */ + FUZZED_MACH_SEND_INTERRUPT = 0x00000040; /* don't restart interrupted sends */ + FUZZED_MACH_SEND_NOTIFY = 0x00000080; /* arm send-possible notify */ + FUZZED_MACH_SEND_ALWAYS = 0x00010000; /* ignore qlimits - kernel only */ + FUZZED_MACH_SEND_TRAILER = 0x00020000; /* sender-provided trailer */ + FUZZED_MACH_SEND_NOIMPORTANCE = 0x00040000; /* msg won't carry importance */ + FUZZED_MACH_SEND_IMPORTANCE = + 0x00080000; /* msg carries importance - kernel only */ + FUZZED_MACH_SEND_SYNC_OVERRIDE = + 0x00100000; /* msg should do sync ipc override */ + FUZZED_MACH_SEND_PROPAGATE_QOS = + 0x00200000; /* IPC should propagate the caller's QoS */ + FUZZED_MACH_SEND_KERNEL = + 0x00400000; /* full send from kernel space - kernel only */ + FUZZED_MACH_SEND_SYNC_BOOTSTRAP_CHECKIN = + 0x00800000; /* special reply port should boost thread doing sync bootstrap + checkin */ + + FUZZED_MACH_RCV_TIMEOUT = 0x00000100; /* timeout value applies to receive */ + FUZZED_MACH_RCV_NOTIFY = 0x00000000; /* legacy name (value was: 0x00000200) */ + FUZZED_MACH_RCV_INTERRUPT = + 0x00000400; /* don't restart interrupted receive */ + FUZZED_MACH_RCV_VOUCHER = 0x00000800; /* willing to receive voucher port */ + // FUZZED_MACH_RCV_OVERWRITE = 0x00000000; /* scatter receive + // (deprecated) */ + FUZZED_MACH_RCV_GUARDED_DESC = + 0x00001000; /* Can receive new guarded descriptor */ + FUZZED_MACH_RCV_SYNC_WAIT = 0x00004000; /* sync waiter waiting for rcv */ + FUZZED_MACH_RCV_SYNC_PEEK = 0x00008000; /* sync waiter waiting to peek */ + + FUZZED_MACH_MSG_STRICT_REPLY = 0x00000200; +} + +enum MachMsgTypeName { + FUZZED_MACH_MSG_TYPE_PORT_NONE = 0; + FUZZED_MACH_MSG_TYPE_PORT_NAME = 15; + FUZZED_MACH_MSG_TYPE_MOVE_RECEIVE = 16; + FUZZED_MACH_MSG_TYPE_MOVE_SEND = 17; + FUZZED_MACH_MSG_TYPE_MOVE_SEND_ONCE = 18; + FUZZED_MACH_MSG_TYPE_COPY_SEND = 19; + FUZZED_MACH_MSG_TYPE_MAKE_SEND = 20; + FUZZED_MACH_MSG_TYPE_MAKE_SEND_ONCE = 21; + FUZZED_MACH_MSG_TYPE_COPY_RECEIVE = 22; + FUZZED_MACH_MSG_TYPE_DISPOSE_RECEIVE = 24; + FUZZED_MACH_MSG_TYPE_DISPOSE_SEND = 25; + FUZZED_MACH_MSG_TYPE_DISPOSE_SEND_ONCE = 26; +} + +enum MachPortRight { + FUZZED_MACH_PORT_RIGHT_SEND = 0; + FUZZED_MACH_PORT_RIGHT_RECEIVE = 1; + FUZZED_MACH_PORT_RIGHT_SEND_ONCE = 2; + FUZZED_MACH_PORT_RIGHT_PORT_SET = 3; + FUZZED_MACH_PORT_RIGHT_DEAD_NAME = 4; + FUZZED_MACH_PORT_RIGHT_LABELH = 5; + FUZZED_MACH_PORT_RIGHT_NUMBER = 6; +} + +enum MachPortOptionsFlag { + MPO_CONTEXT_AS_GUARD = 0x01; /* Add guard to the port */ + MPO_QLIMIT = 0x02; /* Set qlimit for the port msg queue */ + MPO_TEMPOWNER = 0x04; /* Set the tempowner bit of the port */ + MPO_IMPORTANCE_RECEIVER = 0x08; /* Mark the port as importance receiver */ + MPO_INSERT_SEND_RIGHT = 0x10; /* Insert a send right for the port */ + MPO_STRICT = 0x20; /* Apply strict guarding for port */ + MPO_DENAP_RECEIVER = 0x40; /* Mark the port as App de-nap receiver */ + MPO_IMMOVABLE_RECEIVE = + 0x80; /* Mark the port as immovable; protected by the guard context */ + MPO_FILTER_MSG = 0x100; /* Allow message filtering */ + MPO_TG_BLOCK_TRACKING = + 0x200; /* Track blocking relationship for thread group during sync IPC */ +} + +message MachMsg { + optional MachMsgHeader header = 1; + // Optional complex descriptors + repeated MachMsgDescriptor body = 2; + optional bytes data = 3; +} + +enum MachEventlinkCreateOption { + FUZZED_MELC_OPTION_NONE = 0; + FUZZED_MELC_OPTION_NO_COPYIN = 1; + FUZZED_MELC_OPTION_WITH_COPYIN = 2; +} + +enum MachEventlinkAssociateOption { + FUZZED_MELA_OPTION_NONE = 0; + FUZZED_MELA_OPTION_ASSOCIATE_ON_WAIT = 1; +} + +enum MachEventlinkDisassociateOption { + FUZZED_MELD_OPTION_NONE = 0; +} + +enum VmProtection { + FUZZED_VM_PROT_NONE = 0; + FUZZED_VM_PROT_READ = 1; + FUZZED_VM_PROT_WRITE = 2; + FUZZED_VM_PROT_EXECUTE = 3; +} + +// Get around 32-bit protobuf limitiation +enum MachMsg2OptionUpperBits { + FUZZED_MACH64_MSG_VECTOR = 0x00000001; + FUZZED_MACH64_SEND_KOBJECT_CALL = 0x00000002; + FUZZED_MACH64_SEND_MQ_CALL = 0x00000004; + FUZZED_MACH64_SEND_ANY = 0x00000008; + FUZZED_MACH64_RCV_LINEAR_VECTOR = 0x10000000; + FUZZED_MACH64_RCV_STACK = 0x20000000; + FUZZED_MACH64_PEEK_MSG = 0x40000000; + // FUZZED_MACH64_MACH_MSG2 = 0x80000000; +} + +enum MachMsg2Option { + // FUZZED_MACH64_MSG_OPTION_USER = 0; + // FUZZED_MACH64_MACH_MSG2 = 1; + // FUZZED_MACH64_SEND_FILTER_NONFATAL = 2; + // FUZZED_MACH64_MSG_VECTOR = 3; + FUZZED_MACH64_MSG_OPTION_NONE = 0x0; + FUZZED_MACH64_SEND_MSG = 0x00000001; + FUZZED_MACH64_RCV_MSG = 0x00000002; + FUZZED_MACH64_RCV_LARGE = 0x00000004; + FUZZED_MACH64_RCV_LARGE_IDENTITY = 0x00000008; + FUZZED_MACH64_SEND_TIMEOUT = 0x00000010; + FUZZED_MACH64_SEND_OVERRIDE = 0x00000020; + FUZZED_MACH64_SEND_INTERRUPT = 0x00000040; + FUZZED_MACH64_SEND_NOTIFY = 0x00000080; + FUZZED_MACH64_SEND_ALWAYS = 0x00010000; + FUZZED_MACH64_SEND_TRAILER = 0x00020000; + FUZZED_MACH64_SEND_NOIMPORTANCE = 0x00040000; + FUZZED_MACH64_SEND_IMPORTANCE = 0x00080000; + FUZZED_MACH64_SEND_SYNC_OVERRIDE = 0x00100000; + FUZZED_MACH64_SEND_PROPAGATE_QOS = 0x00200000; + FUZZED_MACH64_SEND_KERNEL = 0x00400000; + FUZZED_MACH64_SEND_SYNC_BOOTSTRAP_CHECKIN = 0x00800000; + FUZZED_MACH64_RCV_TIMEOUT = 0x00000100; + // FUZZED_MACH64_RCV_NOTIFY = 0x00000000; + FUZZED_MACH64_RCV_INTERRUPT = 0x00000400; + FUZZED_MACH64_RCV_VOUCHER = 0x00000800; + // FUZZED_MACH64_RCV_OVERWRITE = 0x00000000; + FUZZED_MACH64_RCV_GUARDED_DESC = 0x00001000; + FUZZED_MACH64_RCV_SYNC_WAIT = 0x00004000; + FUZZED_MACH64_RCV_SYNC_PEEK = 0x00008000; + FUZZED_MACH64_MSG_STRICT_REPLY = 0x00000200; +} + +message MachMsg2Trap { + optional MachMsg data = 1; + // optional uint64 options = 2; + repeated MachMsg2Option options = 2; + repeated MachMsg2OptionUpperBits options_upper_bits = 3; + // TODO(nedwill): aren't all of these options already inside the MachMsg header? + optional uint64 msgh_bits_and_send_size = 4; + optional MachPortName msgh_remote_port = 5; + optional MachPortName msgh_local_port = 6; + optional MachPortName msgh_voucher_port = 7; + optional uint32 msgh_id = 8; + optional uint32 desc_count = 9; + optional MachPortName rcv_name = 10; + optional uint32 rcv_size = 11; + optional int32 priority = 12; + optional uint64 timeout = 13; +} diff --git a/fuzz/proto/mach_traps.proto b/fuzz/proto/mach_traps.proto new file mode 100644 index 0000000..f92775f --- /dev/null +++ b/fuzz/proto/mach_traps.proto @@ -0,0 +1,247 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +import "fuzz/proto/mach_message.proto"; +import "third_party/bootstrap_cmds/mig_generated.proto"; + +message MkTimerCreateTrap {} + +message MkTimerDestroyTrap { + optional MachPortName name = 1; +} + +message MkTimerArmTrap { + optional MachPortName name = 1; + optional uint64 expire_time = 2; +} + +message MkTimerCancelTrap { + optional MachPortName name = 1; + // addr + // optional uint64 result_time = 2; +} + +message MkTimerArmLeewayTrap { + optional MachPortName name = 1; + optional uint64 mk_timer_flags = 2; + optional uint64 expire_time = 3; + optional uint64 mk_leeway = 4; +} + +message HostCreateMachVoucherTrap { + optional MachPortName host = 1; + optional bytes recipes = 2; + optional uint64 addr = 3; +} + +message MachVoucherExtractAttrRecipeTrap { + optional MachPortName voucher_name = 1; + optional uint32 key = 2; + optional bytes recipe = 3; +} + +message MachVmAllocateTrap { + // MachPortName target + optional MachPortName target = 1; + optional uint64 addr = 2; + optional uint64 size = 3; + optional int32 flags = 4; +} + +message MachVmPurgableControl { + optional MachPortName target = 1; + optional uint64 address = 2; + optional int32 control = 3; + optional uint64 state = 4; +} + +message MachVmDeallocateTrap { + optional MachPortName target = 1; + optional uint64 address = 2; + optional uint64 size = 3; +} + +message TaskDyldProcessInfoNotifyGet { + optional uint64 names_addr = 1; + optional uint64 names_count_addr = 2; +} + +message MachVmProtectTrap { + optional MachPortName target = 1; + optional uint64 address = 2; + optional uint64 size = 3; + optional bool set_maximum = 4; + repeated VmProtection new_protection = 5; +} + +message MachVmMapTrap { + optional MachPortName target = 1; + optional uint64 addr = 2; + optional uint64 size = 3; + optional int32 mask = 4; + optional int32 flags = 5; + repeated VmProtection cur_protection = 6; +} + +message MachPortAllocateTrap { + optional MachPortName target = 1; + optional MachPortRight right = 2; + // optional uint64 name = 3; +} + +message MachPortDeallocateTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; +} + +message MachPortModRefsTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; + optional MachPortRight right = 3; + optional int32 delta = 4; +} + +message MachPortMoveMemberTrap { + optional MachPortName target = 1; + optional MachPortName member = 2; + optional MachPortName after = 3; +} + +message MachPortInsertRightTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; + optional MachPortName poly = 3; + optional MachMsgTypeName polypoly = 4; +} + +message MachPortInsertMemberTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; + optional MachPortName pset = 3; +} + +message MachPortExtractMemberTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; + optional MachPortName pset = 3; +} + +message MachPortLimits { + optional uint32 limit = 1; +} + +message MachPortOptions { + repeated MachPortOptionsFlag flags = 1; + optional MachPortLimits limits = 2; + optional MachPortName work_interval_port = 3; +} + +message MachPortConstructTrap { + optional MachPortName target = 1; + optional MachPortOptions options = 2; + optional uint64 context = 3; + // optional uint64 name = 4; +} + +message MachPortDestructTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; + optional int32 srdelta = 3; + optional uint64 guard = 4; +} + +message MachReplyPort {} +message ThreadSelf {} +message TaskSelf {} +message HostSelf {} + +message MachMsgOverwrite { + // optional uint64 msg = 1; + optional MachMsg msg = 1; + repeated MachMsgOverwriteOption options = 2; + // optional uint32 send_size = 3; + optional uint32 rcv_size = 3; + optional MachPortName rcv_name = 4; + optional int32 timeout = 5; + optional int32 priority = 6; + optional uint64 rcv_msg = 7; +} + +message MigRoutineMachMsg { + optional MigRoutine routine = 1; + repeated MachMsgOverwriteOption options = 2; + // optional uint32 send_size = 3; + optional uint32 rcv_size = 3; + optional MachPortName rcv_name = 4; + optional int32 timeout = 5; + optional int32 priority = 6; + optional uint64 rcv_msg = 7; + // TODO(nedwill): synthetic part of the message to fuzz port numbers + // MIG itself doesn't have a notion of what to use here + optional MachPortName remote_port = 8; + optional MachPortName local_port = 9; + optional MachPortName voucher_port = 10; + optional MachMsgTypeName remote_type = 11; + optional MachMsgTypeName local_type = 12; + optional MachMsgTypeName voucher_type = 13; +} + +message VmFaultException { + // TODO(nedwill): use a custom address type with better probabilities + optional uint64 addr = 1; + optional VmProtection type = 2; +} + +// TODO(nedwill): cover more cases from third_party/xnu/osfmk/arm64/sleh.c +message ProcessorException { + oneof exception { + VmFaultException vm_fault = 1; + } +} + +enum NotificationId { + MACH_NOTIFY_FIRST = 64; + MACH_NOTIFY_PORT_DELETED = 65; + MACH_NOTIFY_SEND_POSSIBLE = 66; + MACH_NOTIFY_PORT_DESTROYED = 69; + MACH_NOTIFY_NO_SENDERS = 70; + MACH_NOTIFY_SEND_ONCE = 71; + MACH_NOTIFY_DEAD_NAME = 72; + MACH_NOTIFY_LAST = 77; +} + +message MachPortRequestNotificationTrap { + optional MachPortName target = 1; + optional MachPortName name = 2; + optional NotificationId msgid = 3; + optional int64 sync = 4; + optional MachPortName notify = 5; + optional MachMsgTypeName notify_poly = 6; +} diff --git a/fuzz/proto/mig_types.proto b/fuzz/proto/mig_types.proto new file mode 100644 index 0000000..a6ce6cd --- /dev/null +++ b/fuzz/proto/mig_types.proto @@ -0,0 +1,171 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +import "fuzz/proto/common.proto"; + +// TODO(nedwill): make this a oneof and have task_self be an additional option +message TaskPort { + optional PortNumberMach port = 1; +} + +message VmProt { + optional bool read = 1; + optional bool write = 2; + optional bool execute = 3; +} + +enum VmInherit { + FUZZER_VM_INHERIT_SHARE = 0; + FUZZER_VM_INHERIT_COPY = 1; + FUZZER_VM_INHERIT_NONE = 2; + FUZZER_VM_INHERIT_DONATE_COPY = 3; +} + +message VmSync { + optional bool asynchronous = 1; + optional bool synchronous = 2; + optional bool invalidate = 3; + optional bool killpages = 4; + optional bool deactivate = 5; + optional bool contiguous = 6; + optional bool reusablepages = 7; +} + +enum VmBehavior { + FUZZED_VM_BEHAVIOR_DEFAULT = 0; + FUZZED_VM_BEHAVIOR_RANDOM = 1; + FUZZED_VM_BEHAVIOR_SEQUENTIAL = 2; + FUZZED_VM_BEHAVIOR_RSEQNTL = 3; + FUZZED_VM_BEHAVIOR_WILLNEED = 4; + FUZZED_VM_BEHAVIOR_DONTNEED = 5; + FUZZED_VM_BEHAVIOR_FREE = 6; + FUZZED_VM_BEHAVIOR_ZERO_WIRED_PAGES = 7; + FUZZED_VM_BEHAVIOR_REUSABLE = 8; + FUZZED_VM_BEHAVIOR_REUSE = 9; + FUZZED_VM_BEHAVIOR_CAN_REUSE = 10; + FUZZED_VM_BEHAVIOR_PAGEOUT = 11; +} + +enum VmMachineAttribute { + FUZZED_MATTR_CACHE = 1; + FUZZED_MATTR_MIGRATE = 2; + FUZZED_MATTR_REPLICATE = 4; +} + +enum VmMachineAttributeVal { + FUZZED_MATTR_VAL_OFF = 0; + FUZZED_MATTR_VAL_ON = 1; + FUZZED_MATTR_VAL_GET = 2; + FUZZED_MATTR_VAL_CACHE_FLUSH = 6; + FUZZED_MATTR_VAL_DCACHE_FLUSH = 7; + FUZZED_MATTR_VAL_ICACHE_FLUSH = 8; + FUZZED_MATTR_VAL_CACHE_SYNC = 9; + FUZZED_MATTR_VAL_GET_INFO = 10; +} + +enum VmPurgable { + FUZZED_VM_PURGABLE_SET_STATE = 0; + FUZZED_VM_PURGABLE_GET_STATE = 1; + FUZZED_VM_PURGABLE_PURGE_ALL = 2; + FUZZED_VM_PURGABLE_SET_STATE_FROM_KERNEL = 3; +} + +enum MachPortFlavor { + FUZZED_MACH_PORT_LIMITS_INFO = 1; + FUZZED_MACH_PORT_RECEIVE_STATUS = 2; + FUZZED_MACH_PORT_DNREQUESTS_SIZE = 3; + FUZZED_MACH_PORT_TEMPOWNER = 4; + FUZZED_MACH_PORT_IMPORTANCE_RECEIVER = 5; + FUZZED_MACH_PORT_DENAP_RECEIVER = 6; + FUZZED_MACH_PORT_INFO_EXT = 7; + FUZZED_MACH_PORT_GUARD_INFO = 8; +} + +message MachPortQos { + optional bool name = 1; + optional bool prealloc = 2; + optional int32 len = 3; +} + +enum HostFlavor { + HOST_BASIC_INFO = 1; /* basic info */ + HOST_SCHED_INFO = 3; /* scheduling info */ + HOST_RESOURCE_SIZES = 4; /* kernel struct sizes */ + HOST_PRIORITY_INFO = 5; /* priority information */ + HOST_SEMAPHORE_TRAPS = 7; /* Has semaphore traps */ + HOST_MACH_MSG_TRAP = 8; /* Has mach_msg_trap */ + HOST_VM_PURGABLE = 9; /* purg'e'able memory info */ + HOST_DEBUG_INFO_INTERNAL = + 10; /* Used for kernel internal development tests only */ + HOST_CAN_HAS_DEBUGGER = 11; + HOST_PREFERRED_USER_ARCH = 12; /* Get the preferred user-space architecture */ +} + +enum ProcessorFlavor { + PROCESSOR_BASIC_INFO = 1; /* basic information */ + PROCESSOR_CPU_LOAD_INFO = 2; /* cpu load information */ + PROCESSOR_PM_REGS_INFO = 0x10000001; /* performance monitor register info */ + PROCESSOR_TEMPERATURE = 0x10000002; /* Processor core temperature */ +} + +message MachTimespec { + optional int32 a1 = 1; + optional int32 a2 = 2; +} + +message MachZoneName { + optional string mzn_name = 1; +} + +message FsobjId { + optional uint32 fid_objno = 1; + optional uint32 fid_generation = 2; +} + +message Fsid { + optional int32 id1 = 1; + optional int32 id2 = 2; +} + +message DyldKernelImageInfo { + optional bytes uuid = 1; + optional FsobjId fs_obj_id = 2; + optional Fsid fsid = 3; + optional uint64 load_addr = 4; +} + +message AuditToken { + optional bytes val = 1; +} + +message SecurityToken { + optional int32 token1 = 1; + optional int32 token2 = 2; +} diff --git a/fuzz/proto/schedule.proto b/fuzz/proto/schedule.proto new file mode 100644 index 0000000..790a27d --- /dev/null +++ b/fuzz/proto/schedule.proto @@ -0,0 +1,20 @@ +// Copyright 2024 Google LLC +// +// 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. + +syntax = "proto2"; + +message Schedule { + required uint32 rand_seed = 1; + required bytes thread_choices = 2; +} diff --git a/fuzz/proto/session.proto b/fuzz/proto/session.proto new file mode 100644 index 0000000..f6e6670 --- /dev/null +++ b/fuzz/proto/session.proto @@ -0,0 +1,96 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +syntax = "proto2"; + +import "fuzz/proto/bsd.proto"; +import "tools/generate_syscall/bsd_syscalls_generated.proto"; +import "fuzz/proto/common.proto"; +import "fuzz/proto/mach_message.proto"; +import "fuzz/proto/mach_traps.proto"; +import "fuzz/proto/schedule.proto"; + +message Session { + repeated Command commands1 = 1; + repeated Command commands2 = 2; + repeated Command commands3 = 3; + required Schedule schedule = 4; + required bytes xnu_data_provider = 5; +} + +message EmptyMessage {} + +message Command { + oneof command { + MachVmAllocateTrap mach_vm_allocate_trap = 1; + MachVmPurgableControl mach_vm_purgable_control = 2; + MachVmDeallocateTrap mach_vm_deallocate_trap = 3; + TaskDyldProcessInfoNotifyGet task_dyld_process_info_notify_get = 4; + MachVmProtectTrap mach_vm_protect_trap = 5; + MachVmMapTrap mach_vm_map_trap = 6; + MachPortAllocateTrap mach_port_allocate = 7; + MachPortDeallocateTrap mach_port_deallocate = 8; + MachPortModRefsTrap mach_port_mod_refs = 9; + MachPortMoveMemberTrap mach_port_move_member = 10; + MachPortInsertRightTrap mach_port_insert_right = 11; + MachPortInsertMemberTrap mach_port_insert_member = 12; + MachPortExtractMemberTrap mach_port_extract_member = 13; + MachPortConstructTrap mach_port_construct = 14; + MachPortDestructTrap mach_port_destruct = 15; + MachReplyPort mach_reply_port = 16; + ThreadSelf thread_self = 17; + TaskSelf task_self = 18; + HostSelf host_self = 19; + MachMsgOverwrite mach_msg = 20; + MachMsgOverwrite mach_msg_overwrite = 21; + HostCreateMachVoucherTrap host_create_mach_voucher_trap = 22; + MachVoucherExtractAttrRecipeTrap mach_voucher_extract_attr_recipe_trap = 23; + EmptyMessage thread_get_special_reply_port = 26; + MkTimerCreateTrap mk_timer_create_trap = 27; + MkTimerDestroyTrap mk_timer_destroy_trap = 28; + MkTimerArmTrap mk_timer_arm_trap = 29; + MkTimerCancelTrap mk_timer_cancel_trap = 30; + MkTimerArmLeewayTrap mk_timer_arm_leeway_trap = 31; + ArbitrarySyscall arb_bsd_syscall = 32; + ArbitrarySyscall arb_mach_trap = 33; + Packet ip_input = 34; + Ioctl ioctl = 35; + IoctlReal ioctl_real = 37; + + ConnectX connectx = 38; + DisconnectX disconnectx = 41; + NecpClientAction necp_client_action = 49; + NecpSessionAction necp_session_action = 51; + MigRoutineMachMsg mig_routine_mach_msg = 64; + KEvent kevent = 66; + ProcessorException processor_exception = 67; + BsdSyscall syscall = 69; + MachPortRequestNotificationTrap mach_port_request_notification_trap = 70; + MachMsg2Trap mach_msg2_trap = 71; + } +} diff --git a/fuzz/target/BUILD.bazel b/fuzz/target/BUILD.bazel new file mode 100644 index 0000000..2cf9a1a --- /dev/null +++ b/fuzz/target/BUILD.bazel @@ -0,0 +1,137 @@ +load( + "@io_bazel_rules_docker//container:container.bzl", + "container_image", +) +load("//:bazel/generic_cc_binary.bzl", "generic_cc_binary") + +generic_cc_binary( + name = "xnu_fuzzer_centipede", + cc_binary_target = ":xnu_fuzzer", + compilation_mode = "opt", + copts = [ + "-fno-builtin", + "-gline-tables-only", + "-fsanitize-coverage=trace-pc-guard,pc-table,control-flow", + ], + linkopts = [ + "-fno-builtin", + "-gline-tables-only", + "-fsanitize-coverage=trace-pc-guard,pc-table,control-flow", + ], + platform_suffix = "centipede", +) + +generic_cc_binary( + name = "xnu_fuzzer_asan_dbg", + cc_binary_target = ":xnu_fuzzer", + compilation_mode = "dbg", + copts = ["-fsanitize=address"], + linkopts = ["-fsanitize=address"], + platform_suffix = "asan", + visibility = [ + "//tools/group_crashes/backend:__pkg__", + "//tools/group_crashes/backend/services:__pkg__", + ], +) + +generic_cc_binary( + name = "xnu_fuzzer_asan_opt", + cc_binary_target = ":xnu_fuzzer", + compilation_mode = "opt", + copts = ["-fsanitize=address"], + linkopts = ["-fsanitize=address"], + platform_suffix = "asan", +) + +# TODO(nedwill): this runs in an unbounded fashion never finishing compilation +# generic_cc_binary( +# name = "xnu_fuzzer_coverage", +# cc_binary_target = ":xnu_fuzzer", +# compilation_mode = "opt", +# copts = [ +# "-fprofile-instr-generate", +# "-fcoverage-mapping", +# ], +# linkopts = ["-fprofile-instr-generate"], +# platform_suffix = "clang-coverage", +# ) + +generic_cc_binary( + name = "centipede", + cc_binary_target = "@com_google_fuzztest//centipede:centipede", + compilation_mode = "dbg", + platform_suffix = "", +) + +py_library( + name = "fuzz_job", + srcs = [ + "fuzz_job.py", + ], +) + +py_test( + name = "fuzz_job_test", + srcs = [ + "fuzz_job_test.py", + ], + deps = [ + ":fuzz_job", + ], +) + +container_image( + name = "app", + # References container_pull from WORKSPACE (above) + base = "@sockfuzzer_base_image//image", + cmd = [ + "python3", + "/fuzz/target/fuzz_job.py", + ], + directory = "/fuzz/target", + env = { + "LD_LIBRARY_PATH": "/lib", + "ASAN_SYMBOLIZER_PATH": "/bin/llvm-symbolizer", + "ASAN_OPTIONS": "detect_leaks=0", + }, + files = [ + ":centipede", + ":fuzz_job", + ":xnu_fuzzer_asan_opt", + ":xnu_fuzzer_centipede", + ], +) + +cc_binary( + name = "xnu_fuzzer", + srcs = ["//fuzz/xnu:libxnu.o"], + linkopts = [ + "-lrt", + ], + deps = [ + ":xnu_fuzzer_lib", + "@com_google_fuzztest//centipede:centipede_runner", + ], +) + +cc_library( + name = "xnu_fuzzer_lib", + srcs = [ + "xnu_fuzzer.cc", + ], + includes = [ + "third_party/libprotobuf-mutator", + ], + visibility = ["//:__pkg__"], + deps = [ + "//fuzz/common:utility", + "//fuzz/handlers:session", + "//fuzz/host/hypercall", + "//third_party/libprotobuf-mutator:libprotobuf_mutator", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/flags:usage", + "@com_google_absl//absl/log", + "@com_google_absl//absl/log:initialize", + ], +) diff --git a/fuzz/target/fuzz_job.py b/fuzz/target/fuzz_job.py new file mode 100755 index 0000000..39f6a76 --- /dev/null +++ b/fuzz/target/fuzz_job.py @@ -0,0 +1,53 @@ +# Copyright 2024 Google LLC +# +# 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. + +# /usr/bin/env python3 +import os +import subprocess + + +def fetch_environment_variables(): + pod_name = os.environ.get("POD_NAME", "fuzz-0") + num_replicas = int(os.environ.get("NUM_REPLICAS", "1")) + threads_per_replica = int(os.environ.get("THREADS_PER_REPLICA", "1")) + return pod_name, num_replicas, threads_per_replica + + +def generate_command(pod_name, num_replicas, threads_per_replica): + try: + pod_index = int(pod_name.split("-")[-1]) * threads_per_replica + except ValueError: + raise ValueError(f"Pod name {pod_name} is in an unexpected format") + total_shards = num_replicas * threads_per_replica + + cmd = [ + "/fuzz/target/centipede", + "--binary=/fuzz/target/xnu_fuzzer_centipede", + "--extra_binaries=/fuzz/target/xnu_fuzzer_asan_opt", + "--workdir=/workdir", + f"--total_shards={total_shards}", + f"--first_shard_index={pod_index}", + f"--num_threads={threads_per_replica}", + ] + return cmd + + +def main(): + pod_name, num_replicas, threads_per_replica = fetch_environment_variables() + cmd = generate_command(pod_name, num_replicas, threads_per_replica) + subprocess.run(cmd) + + +if __name__ == "__main__": + main() diff --git a/fuzz/target/fuzz_job_test.py b/fuzz/target/fuzz_job_test.py new file mode 100644 index 0000000..fa95978 --- /dev/null +++ b/fuzz/target/fuzz_job_test.py @@ -0,0 +1,55 @@ +# Copyright 2024 Google LLC +# +# 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. + +import os +import unittest +from unittest.mock import patch +from sockfuzzer.fuzz.target import fuzz_job + +class FuzzJobTest(unittest.TestCase): + + @patch.dict(os.environ, {"POD_NAME": "fuzzer-3", "NUM_REPLICAS": "4", "THREADS_PER_REPLICA": "3"}) + def test_fetch_environment_variables(self): + pod_name, num_replicas, threads_per_replica = fuzz_job.fetch_environment_variables() + self.assertEqual(pod_name, "fuzzer-3") + self.assertEqual(num_replicas, 4) + self.assertEqual(threads_per_replica, 3) + + def test_generate_command(self): + cmd = fuzz_job.generate_command("fuzzer-3", 4, 3) + expected_cmd = [ + "/fuzz/target/centipede", + "--binary=/fuzz/target/xnu_fuzzer_centipede", + "--extra_binaries=/fuzz/target/xnu_fuzzer_asan_opt", + "--workdir=/workdir", + "--total_shards=12", + "--first_shard_index=9", + "--num_threads=3", + ] + self.assertEqual(cmd, expected_cmd) + + @patch('sockfuzzer.fuzz.target.fuzz_job.subprocess.run') + @patch('sockfuzzer.fuzz.target.fuzz_job.generate_command') + @patch('sockfuzzer.fuzz.target.fuzz_job.fetch_environment_variables') + def test_main(self, mock_fetch_env, mock_generate_cmd, mock_run_cmd): + mock_fetch_env.return_value = ("fuzzer-3", 2, 2) + mock_generate_cmd.return_value = ["mock_cmd"] + fuzz_job.main() + mock_fetch_env.assert_called_once() + mock_generate_cmd.assert_called_once_with("fuzzer-3", 2, 2) + mock_run_cmd.assert_called_once_with(["mock_cmd"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/fuzz/target/xnu_fuzzer.cc b/fuzz/target/xnu_fuzzer.cc new file mode 100644 index 0000000..fa03cb4 --- /dev/null +++ b/fuzz/target/xnu_fuzzer.cc @@ -0,0 +1,239 @@ +// Copyright 2024 Google LLC +// +// 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/debugging/symbolize.h" +#include "absl/flags/flag.h" +#include "absl/flags/marshalling.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "absl/log/initialize.h" +#include "absl/log/log.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "fuzz/common/utility.h" +#include "fuzz/handlers/session.h" +#include "fuzz/host/host.h" +#include "third_party/libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h" + +namespace absl::flags_internal { +enum class ArgvListAction; +enum class OnUndefinedFlag; +enum class UsageFlagsAction; +} // namespace absl::flags_internal +namespace google::protobuf { +class Message; +} // namespace google::protobuf + +#ifdef LIMIT_CALLGRIND_SCOPE +#include +#endif + +extern "C" { + +using proc_t = struct proc *; +using task_t = struct task *; + +using boolean_t = int; +using ast_t = int; +} + +extern int nprocs; + +static std::string ProtobufToString(const google::protobuf::Message &message) { + std::string result; + google::protobuf::TextFormat::Printer printer; + printer.SetHideUnknownFields(true); + printer.PrintToString(message, &result); + return result; +} + +// This should be a plaintext Session protobuf. The commands will be kept constant +// as the scheduler and xnu data are fuzzed. +struct SingleTestcaseSession { + explicit SingleTestcaseSession(std::optional path) + : path(std::move(path)) {} + + std::optional path; + Session session; +} __attribute__((packed)) __attribute__((aligned(128))); + +std::string AbslUnparseFlag(const SingleTestcaseSession &testcase) { + return absl::UnparseFlag(testcase.path); +} + +bool AbslParseFlag(absl::string_view text, SingleTestcaseSession *testcase, + std::string *error) { + // Check if path is provided + // std::optional path_option; + if (!absl::ParseFlag(text, &testcase->path, error)) { + return false; + } + if (!testcase->path.has_value()) { + return true; + } + const std::string &path = testcase->path.value(); + + // Read testcase data + absl::StatusOr> testcase_bytes_or = ReadFile(path); + if (!testcase_bytes_or.ok()) { + *error = testcase_bytes_or.status().message(); + return false; + } + const auto &testcase_bytes = testcase_bytes_or.value(); + + // Attempt to parse protobuf data + constexpr bool binary = true; + if (protobuf_mutator::libfuzzer::LoadProtoInput( + !binary, testcase_bytes.data(), testcase_bytes.size(), + &testcase->session)) { + return true; + } + + // See if a binary input was provided by accident + // If so, print out a text version + if (protobuf_mutator::libfuzzer::LoadProtoInput(binary, testcase_bytes.data(), + testcase_bytes.size(), + &testcase->session)) { + *error = absl::StrFormat("Binary input provided for %s", path); + std::string session_as_text = ProtobufToString(testcase->session); + std::cerr << session_as_text; + } else { + *error = absl::StrFormat("Failed to parse protobuf input %s", path); + } + + return false; +} + +ABSL_FLAG(SingleTestcaseSession, testcase, SingleTestcaseSession(std::nullopt), + "Fuzz only data_provider and scheduler fields for a given testcase"); +ABSL_FLAG(std::optional, write_text_protobuf, std::nullopt, + "Write text protobuf to provided path from the testcase specified " + "with --testcase."); +ABSL_FLAG(bool, verbose, false, "Enable verbose testcase output"); + +std::vector new_argv; +std::vector> flag_names_storage; + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + absl::SetProgramUsageMessage("use --helpfull to see options"); + + std::vector positional_args; + std::vector unrecognized_flags; + absl::ParseAbseilFlagsOnly(*argc, *argv, positional_args, unrecognized_flags); + + absl::InitializeSymbolizer(**argv); + is_verbose = absl::GetFlag(FLAGS_verbose); + absl::InitializeLog(); + InitializeHost(); +#ifdef NDEBUG + if (is_verbose) { + LOG(FATAL) << "--verbose flag not supported with optimized builds"; + } +#endif + + new_argv.reserve(positional_args.size() + unrecognized_flags.size()); + + int new_argc = 0; + for (char *positional_arg : positional_args) { + new_argv[new_argc++] = positional_arg; + } + + for (const absl::UnrecognizedFlag &unrecognized_flag : unrecognized_flags) { + std::string flag_name_with_dashes = + absl::StrFormat("--%s", unrecognized_flag.flag_name); + auto flag_name_with_dashes_storage = + std::make_unique(flag_name_with_dashes.size() + 1); + memcpy(flag_name_with_dashes_storage.get(), flag_name_with_dashes.data(), + flag_name_with_dashes.size() + 1); + + new_argv[new_argc++] = flag_name_with_dashes_storage.get(); + flag_names_storage.push_back(std::move(flag_name_with_dashes_storage)); + } + + *argv = new_argv.data(); + *argc = new_argc; + + return 0; +} + +DEFINE_BINARY_PROTO_FUZZER(const Session &input_session) { + const Session *session = &input_session; + +#ifdef LIMIT_CALLGRIND_SCOPE + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_TOGGLE_COLLECT; +#endif + + // TODO(nedwill): these flag checks are costly and should not be compiled + // in for large-scale fuzzing builds. We only want them when doing minimization + // and PoC creation. We should have a separate build flag for that. + auto testcase = absl::GetFlag(FLAGS_testcase); + if (testcase.path.has_value()) { + // Use XNU data provider and scheduler data with existing session. + testcase.session.set_xnu_data_provider(input_session.xnu_data_provider()); + testcase.session.mutable_schedule()->set_rand_seed( + input_session.schedule().rand_seed()); + testcase.session.mutable_schedule()->set_thread_choices( + input_session.schedule().thread_choices()); + session = &testcase.session; + } + + // TODO(nedwill): minimization only + auto write_text_protobuf = absl::GetFlag(FLAGS_write_text_protobuf); + if (write_text_protobuf.has_value()) { + std::string path = write_text_protobuf.value(); + std::ofstream out(path); + if (!out.is_open()) { + std::cout << "Failed to open file for --write_text_protobuf: " << path + << std::endl; + return; + } + + std::string text_output; + if (!google::protobuf::TextFormat::PrintToString(*session, &text_output)) { + std::cout << "Failed to convert protobuf to text format for " + "--write_text_protobuf" + << std::endl; + return; + } + + out << text_output; + if (!out) { + std::cout << "Failed to write text protobuf to file: " << path + << std::endl; + return; + } + + std::cout << "Successfully wrote text protobuf to: " << path << std::endl; + } + + if (is_verbose) { + std::cout << ProtobufToString(*session); + } + + HandleSession(session); +} diff --git a/fuzz/xnu/BUILD.bazel b/fuzz/xnu/BUILD.bazel new file mode 100644 index 0000000..5ba0153 --- /dev/null +++ b/fuzz/xnu/BUILD.bazel @@ -0,0 +1,48 @@ +load("//:bazel/cc_object.bzl", "cc_object") + +LIBXNU_DEPS = [ + "//fuzz/xnu/bsd:api", + "//fuzz/xnu/bsd:fakes", + "//fuzz/xnu/osfmk:api", + "//fuzz/xnu/osfmk:fakes", + "//fuzz/xnu/osfmk:sync_fakes", + "//third_party/xnu:libkern_internal", + "//third_party/xnu:security_internal", + "//fuzz/host/hypercall:headers", + "@zlib//:zlib", + # "//fuzz/xnu/test:backend", +] + select({ + "//:asan_enabled": ["//fuzz/xnu/san:san"], + "//conditions:default": [], +}) + +cc_object( + name = "libxnu.o", + redefine_syms = "libxnu.redef", + visibility = [ + "//fuzz/host:__pkg__", + "//fuzz/target:__pkg__", + ], + deps = LIBXNU_DEPS, +) + +cc_binary( + name = "libxnu.so", + additional_linker_inputs = [":xnu_version_script"], + linkopts = [ + "-Wl,--version-script=$(location :xnu_version_script)", + "-Wl,--no-gc-sections", + ], + linkshared = True, + visibility = [ + "//fuzz/host:__pkg__", + "//fuzz/target:__pkg__", + ], + deps = LIBXNU_DEPS, +) + +filegroup( + name = "xnu_version_script", + srcs = ["xnu_version_script.lds"], + visibility = ["//visibility:public"], +) diff --git a/fuzz/xnu/bsd/BUILD.bazel b/fuzz/xnu/bsd/BUILD.bazel new file mode 100644 index 0000000..7c3fdb4 --- /dev/null +++ b/fuzz/xnu/bsd/BUILD.bazel @@ -0,0 +1,53 @@ +load("//third_party/xnu:options.bzl", "BSD_DEFINES", "BSD_INCLUDES", "XNU_COMPILE_OPTIONS", "XNU_INCLUDES", "convert_includes_to_flags") + +cc_library( + name = "headers", + hdrs = [ + "api/backend.h", + "api/ioctl.h", + "//tools/generate_syscall:syscall_wrappers_generated.h", + ], + visibility = ["//fuzz:__subpackages__"], +) + +cc_library( + name = "api", + srcs = [ + "api/bsd_backend.c", + "api/ioctl.c", + "//tools/generate_syscall:syscall_wrappers_generated.c", + ], + # TODO(nedwill): use this when making header dependency change below + # copts = XNU_COMPILE_OPTIONS_NO_META, + copts = BSD_DEFINES + convert_includes_to_flags(BSD_INCLUDES + XNU_INCLUDES) + XNU_COMPILE_OPTIONS, + linkstatic = 1, + visibility = ["//fuzz/xnu:__pkg__"], + deps = [ + ":headers", + "//fuzz/host/hypercall:headers", + "//fuzz/xnu/osfmk:fake_headers", + "//third_party/xnu:bsd_internal_headers", + ], + alwayslink = 1, +) + +cc_library( + name = "fakes", + srcs = [ + "fakes/fake_impls.c", + "fakes/mbuf.c", + "fakes/mcache.c", + "fakes/platform_expert.c", + "fakes/root_device.c", + "fakes/stubs.c", + "fakes/vfs.c", + ], + copts = BSD_DEFINES + convert_includes_to_flags(BSD_INCLUDES + XNU_INCLUDES) + XNU_COMPILE_OPTIONS, + linkstatic = 1, + visibility = ["//fuzz/xnu:__pkg__"], + deps = [ + ":headers", + "//third_party/xnu:bsd_internal", + ], + alwayslink = 1, +) diff --git a/fuzz/xnu/bsd/api/backend.h b/fuzz/xnu/bsd/api/backend.h new file mode 100644 index 0000000..b4ef5d2 --- /dev/null +++ b/fuzz/xnu/bsd/api/backend.h @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_BACKEND_H_ +#define FUZZ_BACKEND_H_ + +#ifdef __cplusplus +#include +extern "C" { +#else +#include +#include +#endif + +void clear_all(); +struct mbuf *get_mbuf_data(const char *data, size_t size, int pktflags); +void ip_input_wrapper(void *m); +void ip6_input_wrapper(void *m); +void XNUDoRandomSyscall(unsigned int number, void *data, size_t size); +bool initialize_kernel(); +void KillAllNonInitProcs(); + +void *XNUCurrentProc(); +bool XNUReapInitChildren(); + +#ifdef __cplusplus +} +#endif + +#endif // FUZZ_BACKEND_H_ diff --git a/fuzz/xnu/bsd/api/bsd_backend.c b/fuzz/xnu/bsd/api/bsd_backend.c new file mode 100644 index 0000000..b177aa4 --- /dev/null +++ b/fuzz/xnu/bsd/api/bsd_backend.c @@ -0,0 +1,235 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/xnu/bsd/api/backend.h" +#include "third_party/xnu/BUILD/obj/EXPORT_HDRS/bsd/sys/sysproto.h" +#include "third_party/xnu/BUILD/obj/EXPORT_HDRS/osfmk/kern/timer_call.h" +#include "third_party/xnu/bsd/sys/kpi_mbuf.h" +#include "third_party/xnu/bsd/sys/sysent.h" + +extern ifnet_t lo_ifp; + +// TODO(nedwill): include all of these via headers +void cpu_init(); +void kernel_bootstrap_thread(void); +void osfmk_bootstrap(); + +void *calloc(size_t nmemb, size_t size); +void free(void *ptr); + +struct mbuf *mbuf_create(const uint8_t *data, size_t size, bool is_header, + bool force_ext, int m_type, int pktflags); +void ip_input(mbuf_t m); +void ip6_input(mbuf_t m); + +const int MT_DATA = 1; + +struct mbuf *get_mbuf_data(const char *data, size_t size, int pktflags) { + struct mbuf *mbuf_data = + mbuf_create((const uint8_t *)data, size, true, false, MT_DATA, pktflags); + + if (!mbuf_data) { + return NULL; + } + + // TODO(nedwill): consider using a non-loopback interface + // This indicates where the packet came from. + mbuf_pkthdr_setrcvif((mbuf_t)mbuf_data, lo_ifp); + return mbuf_data; +} + +bool initialize_kernel() { + kernel_startup_bootstrap(); + timer_call_init(); + cpu_init(); + osfmk_bootstrap(); + kernel_bootstrap(); + return true; +} + +void ip_input_wrapper(void *m) { + ip_input((mbuf_t)m); +} + +void ip6_input_wrapper(void *m) { + ip6_input((mbuf_t)m); +} + +// typedef struct task* task_t; + +task_t get_init_task() { + return proc_task(initproc); +} + +int printf(const char *format, ...); +void proc_exit(proc_t p); + +void KillAllNonInitProcs() { + proc_list_lock(); + proc_t p = NULL; + pid_t *pids = calloc(nprocs, sizeof(pid_t)); + size_t n = 0; + ALLPROC_FOREACH(p) { + if (p == kernproc || p == initproc) { + continue; + } + pid_t pid = p->p_pid; + pids[n++] = pid; + } + proc_list_unlock(); + + for (int i = 0; i < n; i++) { + int rv = 0; + struct kill_args args = { + .pid = pids[i], + .signum = SIGKILL, + .posix = 0, + }; + kill(current_proc(), &args, &rv); + GetHypercallInterface()->ThreadPrintf( + "KillAllNonInitProcs: SIGKILL called on %d\n", pids[i]); + } + free(pids); + + // We now check that the pids were actually killed. + // Uninterruptible threads cannot be killed, and kill() doesn't report an error. + // We want to avoid unkillable procs building up and slowing down the fuzzer. + proc_list_lock(); + n = 0; + ALLPROC_FOREACH(p) { + if (p == kernproc || p == initproc) { + continue; + } + n++; + } + proc_list_unlock(); + if (n > 0) { + GetHypercallInterface()->ThreadPrintf( + "Failed to kill %d procs in KillAllNonInitProcs\n", n); + // panic(); + } +} + +enum { + // TODO(nedwill): fix nosys signal handling + NOSYS_SYSNO = 0, + // TODO(nedwill): can't call exit more than once on same thread + EXIT_SYSNO = 1, + // TODO(nedwill): causes infinite loop killing non-init procs + FORK_SYSNO = 2, + // Banned until we support exit. + PTRACE_SYSNO = 26, + // TODO(nedwill): causes panic when reaping children + POSIX_SPAWN_SYSNO = 244, + // TODO(nedwill): crashes with null derefs + SEMSYS_SYSNO = 251, + SEMGET_SYSNO = 255, + // TODO(nedwill): crashes due to bad signal number + LIO_LISTIO_SYSNO = 320, + // TODO(nedwill): crashes due to interactions with invalid waitqs + PSELECT_SYSNO = 394, +}; + +extern const char *syscallnames[]; + +void XNUDoRandomSyscall(unsigned int number, void *data, size_t size) { + number %= nsysent; + switch (number) { + case FORK_SYSNO: + case PTRACE_SYSNO: + case NOSYS_SYSNO: + case POSIX_SPAWN_SYSNO: + case EXIT_SYSNO: + case LIO_LISTIO_SYSNO: + case PSELECT_SYSNO: + case SEMSYS_SYSNO: + case SEMGET_SYSNO: + return; + default: + break; + } + const struct sysent *entry = &sysent[number]; + size_t argument_size = (size_t)(entry->sy_narg * 8); + void *arguments = calloc(1, argument_size); + if (!arguments) { + return; + } + if (size > argument_size) { + size = argument_size; + } + memcpy(arguments, data, size); + user_ssize_t retval = 0; + GetHypercallInterface()->BlockCopyin(); + proc_t proc = current_proc(); + GetHypercallInterface()->ThreadPrintf( + "[SYSCALL] (XNUDoRandomSyscall): %s (%d)\n", syscallnames[number], + number); + entry->sy_call(proc, arguments, &retval); + GetHypercallInterface()->ThreadPrintf( + "[/SYSCALL] (XNUDoRandomSyscall): %s returned %d\n", syscallnames[number], + retval); + GetHypercallInterface()->UnblockCopyin(); + free(arguments); +} + +void *XNUCurrentProc() { + return current_proc(); +} + +bool XNUReapInitChildren() { + int retval = 0; + struct wait4_args uap = { + .pid = -1, + .status = 0, + .options = WNOHANG, + .rusage = 0, + }; + while (!LIST_EMPTY(&zombproc)) { + int rv = wait4(initproc, &uap, &retval); + if (!retval) { + // panic("Failed to reap child"); + return false; + } + } + return false; +} diff --git a/fuzz/api/ioctl.c b/fuzz/xnu/bsd/api/ioctl.c similarity index 92% rename from fuzz/api/ioctl.c rename to fuzz/xnu/bsd/api/ioctl.c index 77a1476..14e5ce4 100644 --- a/fuzz/api/ioctl.c +++ b/fuzz/xnu/bsd/api/ioctl.c @@ -26,16 +26,20 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -#include "bsd/net/if.h" -#include "bsd/net/network_agent.h" -#include "bsd/net/pfvar.h" -#include "bsd/netinet6/in6_var.h" -#include "bsd/netinet6/nd6.h" -#include "bsd/sys/sockio.h" +#include "fuzz/xnu/bsd/api/ioctl.h" + +#include + +#include "third_party/xnu/bsd/net/if.h" +#include "third_party/xnu/bsd/net/network_agent.h" +#include "third_party/xnu/bsd/net/pfvar.h" +#include "third_party/xnu/bsd/netinet6/in6_var.h" +#include "third_party/xnu/bsd/netinet6/nd6.h" +#include "third_party/xnu/bsd/sys/sockio.h" #define SIOCGIFORDER _IOWR('i', 179, struct if_order) -__attribute__((visibility("default"))) unsigned long ioctls[281] = { +unsigned long ioctls[] = { SIOCSHIWAT, SIOCGHIWAT, SIOCSLOWAT, @@ -319,11 +323,8 @@ __attribute__((visibility("default"))) unsigned long ioctls[281] = { DIOCGIFSPEED, }; -__attribute__((visibility("default"))) const int num_ioctls = - sizeof(ioctls) / sizeof(unsigned long); +const int num_ioctls = sizeof(ioctls) / sizeof(unsigned long); // Export these enums so they are accessible outside libxnu. -__attribute__((visibility("default"))) const unsigned long siocaifaddr_in6_64 = - SIOCAIFADDR_IN6_64; -__attribute__((visibility("default"))) const unsigned long siocsifflags = - SIOCSIFFLAGS; +const unsigned long siocaifaddr_in6_64 = SIOCAIFADDR_IN6_64; +const unsigned long siocsifflags = SIOCSIFFLAGS; diff --git a/fuzz/xnu/bsd/api/ioctl.h b/fuzz/xnu/bsd/api/ioctl.h new file mode 100644 index 0000000..cbac2d2 --- /dev/null +++ b/fuzz/xnu/bsd/api/ioctl.h @@ -0,0 +1,37 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef IOCTL_H_ +#define IOCTL_H_ + +extern unsigned long ioctls[]; +extern const int num_ioctls; +extern const unsigned long siocaifaddr_in6_64; +extern const unsigned long siocsifflags; + +#endif diff --git a/fuzz/xnu/bsd/fakes/fake_impls.c b/fuzz/xnu/bsd/fakes/fake_impls.c new file mode 100644 index 0000000..7faf53c --- /dev/null +++ b/fuzz/xnu/bsd/fakes/fake_impls.c @@ -0,0 +1,162 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// Trivial implementations belong here. More substantial faked +// subsystems should live in their own file. + +#include +#include +#include +#include +#include +#include +#include + +#include "bsd/sys/malloc.h" +#include "bsd/uuid/uuid.h" + +#include "fuzz/host/hypercall/hypercall.h" + +bool PE_parse_boot_argn(const char *arg_string, void *arg_ptr, int max_arg) { + if (!strcmp(arg_string, "ifa_debug")) { + *(bool *)arg_ptr = false; + return true; + } + + if (!strcmp(arg_string, "inaddr_nhash")) { + *(uint32_t *)arg_ptr = 0; + return true; + } + + if (!strcmp(arg_string, "mcache_flags")) { + *(uint32_t *)arg_ptr = 0; + return true; + } + + if (!strcmp(arg_string, "mbuf_debug")) { + *(uint32_t *)arg_ptr = 0; + return true; + } + + if (!strcmp(arg_string, "mleak_sample_factor")) { + *(uint32_t *)arg_ptr = 0; + return true; + } + + // assert(false); + return false; +} + +void *os_log_create() { + return (void *)1; +} + +void uuid_clear(uuid_t uu) { + memset(uu, 0, sizeof(uuid_t)); +} + +int uuid_is_null(const uuid_t uu) { + return !memcmp(uu, UUID_NULL, sizeof(uuid_t)); +} + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) { + return memcmp(uu1, uu2, sizeof(uuid_t)); +} + +// TODO(nedwill): this shouldn't return the same value +// within the same fuzz session (use a counter and reset) +void uuid_generate_random(uuid_t out) { + if (GetHypercallInterface()->GetFuzzedBool()) { + memcpy(out, "0000000000000000", 16); + return; + } + if (GetHypercallInterface()->GetFuzzedBool()) { + memcpy(out, "1111111111111111", 16); + return; + } + memcpy(out, "2222222222222222", 16); +} + +void uuid_copy(uuid_t dst, const uuid_t src) { + memcpy(dst, src, sizeof(uuid_t)); +} + +void uuid_unparse_upper(const uuid_t uu, uuid_string_t out) { + snprintf(out, sizeof(uuid_string_t), + "%c%c%c%c-" + "%c%c-" + "%c%c-" + "%c%c-" + "%c%c%c%c%c%c", + uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9], + uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); +} + +void uuid_unparse(const uuid_t uu, uuid_string_t out) { + uuid_unparse_upper(uu, out); +} + +// unsigned long RandomULong() { +// // returning 0 here would be a failure +// return 1; +// } + +void *malloc(size_t size); +void free(void *ptr); + +void *__MALLOC(size_t size, int type, int flags, vm_allocation_site_t *site) { + void *addr = NULL; + assert(type < M_LAST); + + if (size == 0) { + return NULL; + } + + addr = malloc(size); + if (!addr) { + return NULL; + } + + if (flags & M_ZERO) { + bzero(addr, size); + } + + return (addr); +} + +void read_frandom(void *buffer, unsigned int numBytes) { + GetHypercallInterface()->GetFuzzedBytes(buffer, numBytes); +} + +void read_random(void *buffer, unsigned int numBytes) { + GetHypercallInterface()->GetFuzzedBytes(buffer, numBytes); +} + +void ovbcopy(const char *from, char *to, size_t nbytes) { + memmove(to, from, nbytes); +} diff --git a/fuzz/xnu/bsd/fakes/mbuf.c b/fuzz/xnu/bsd/fakes/mbuf.c new file mode 100644 index 0000000..e27ce0a --- /dev/null +++ b/fuzz/xnu/bsd/fakes/mbuf.c @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +#include + +#include + +struct mbuf *mbuf_create(const uint8_t *data, size_t size, bool is_header, + bool force_ext, int mtype, int pktflags) { + struct mbuf *m; + if (is_header) { + m = m_gethdr(M_DONTWAIT, mtype); + } else { + m = m_get(M_DONTWAIT, mtype); + } + if (!m) { + return m; + } + + if (size > m->m_len) { + size = m->m_len; + } + + /* populate a new mbuf containing the vlan ethernet header */ + m->m_len = size; + memcpy((void*)m->m_data, data, size); + return m; +} diff --git a/fuzz/xnu/bsd/fakes/mcache.c b/fuzz/xnu/bsd/fakes/mcache.c new file mode 100644 index 0000000..8ec8fa8 --- /dev/null +++ b/fuzz/xnu/bsd/fakes/mcache.c @@ -0,0 +1,225 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +struct timeval; + +void *calloc(size_t nmemb, size_t size); +void *malloc(size_t size); +void free(void *ptr); + +int printf(const char *, ...) __printflike(1, 2); + +int assfail(const char *a, const char *f, int l) { + printf("assertion failed: %s, file: %s, line: %d", a, f, l); + GetHypercallInterface()->Abort(); +} + +typedef enum { + MC_MBUF = 0, /* Regular mbuf */ + MC_CL, /* Cluster */ + MC_BIGCL, /* Large (4KB) cluster */ + MC_16KCL, /* Jumbo (16KB) cluster */ + MC_MBUF_CL, /* mbuf + cluster */ + MC_MBUF_BIGCL, /* mbuf + large (4KB) cluster */ + MC_MBUF_16KCL /* mbuf + jumbo (16KB) cluster */ +} mbuf_class_t; + +void *mcache_alloc(mcache_t *cp, int wait) { + mcache_obj_t *buf; + + mcache_alloc_ext(cp, &buf, 1, wait); + return buf; +} + +extern unsigned int mbuf_cslab_alloc(void *, mcache_obj_t ***, unsigned int, + int); + +#define MBUF_INIT_PKTHDR(m) \ + { \ + (m)->m_pkthdr.rcvif = NULL; \ + (m)->m_pkthdr.pkt_hdr = NULL; \ + (m)->m_pkthdr.len = 0; \ + (m)->m_pkthdr.csum_flags = 0; \ + (m)->m_pkthdr.csum_data = 0; \ + (m)->m_pkthdr.vlan_tag = 0; \ + (m)->m_pkthdr.comp_gencnt = 0; \ + m_classifier_init(m, 0); \ + m_tag_init(m, 1); \ + m_scratch_init(m); \ + } + +#define MBUF_INIT(m, pkthdr, type) \ + { \ + (m)->m_next = (m)->m_nextpkt = NULL; \ + (m)->m_len = 0; \ + (m)->m_type = type; \ + if ((pkthdr) == 0) { \ + (m)->m_data = (uintptr_t)(m)->m_dat; \ + (m)->m_flags = 0; \ + } else { \ + (m)->m_data = (uintptr_t)(m)->m_pktdat; \ + (m)->m_flags = M_PKTHDR; \ + MBUF_INIT_PKTHDR(m); \ + } \ + } + +void *aligned_alloc(size_t alignment, size_t size); + +unsigned int mcache_alloc_ext(mcache_t *cp, mcache_obj_t **list, + unsigned int num, int wait) { + bool is_composite = ((int)cp->mc_private == MC_MBUF_CL || + (int)cp->mc_private == MC_MBUF_BIGCL || + (int)cp->mc_private == MC_MBUF_16KCL); + if (cp->mc_slab_alloc && is_composite) { + return cp->mc_slab_alloc(cp->mc_private, &list, num, wait); + } + + mcache_obj_t *obj = NULL; + mcache_obj_t *next = NULL; + for (unsigned int i = 0; i < num; i++) { + // printf("mcache_alloc_ext with bufsize %d\n", cp->mc_bufsize); + if (cp->mc_bufsize == 256) { + obj = aligned_alloc(cp->mc_bufsize, cp->mc_bufsize); + } else { + obj = malloc(cp->mc_bufsize); + } + if ((int)cp->mc_private == MC_MBUF) { + MBUF_INIT((struct mbuf *)obj, 0, MT_FREE); + } + obj->obj_next = next; + next = obj; + } + + *list = obj; + return num; +} + +mcache_t *mcache_audit_cache; +void mcache_audit_free_verify(mcache_audit_t *a1, void *a2, size_t a3, + size_t a4) { + GetHypercallInterface()->Abort(); +} +void mcache_audit_free_verify_set(mcache_audit_t *a1, void *a2, size_t a3, + size_t a4) { + GetHypercallInterface()->Abort(); +} +boolean_t mcache_bkt_isempty(mcache_t *cp) { + return true; +} +void mcache_buffer_log(mcache_audit_t *a1, void *a2, mcache_t *a3, + struct timeval *a4) { + GetHypercallInterface()->Abort(); +} + +mcache_t *mcache_create(const char *name, size_t bufsize, size_t align, + u_int32_t flags, int wait __unused) { + mcache_t *cache = calloc(1, sizeof(mcache_t)); + strncpy(cache->mc_name, name, sizeof(cache->mc_name)); + cache->mc_bufsize = bufsize; + cache->mc_slab_alloc = NULL; + cache->mc_slab_free = NULL; + cache->mc_slab_audit = NULL; + cache->mc_private = cache; + return cache; +} + +mcache_t *mcache_create_ext(const char *name, size_t bufsize, + mcache_allocfn_t allocfn, mcache_freefn_t freefn, + mcache_auditfn_t auditfn, mcache_logfn_t logfn, + mcache_notifyfn_t notifyfn, void *arg, + u_int32_t flags, int wait __unused) { + mcache_t *cache = calloc(1, sizeof(mcache_t)); + strncpy(cache->mc_name, name, sizeof(cache->mc_name)); + cache->mc_bufsize = bufsize; + cache->mc_slab_alloc = allocfn; + cache->mc_slab_free = freefn; + cache->mc_slab_audit = auditfn; + cache->mc_private = arg; + return cache; +} + +char *mcache_dump_mca(char buf[DUMP_MCA_BUF_SIZE], mcache_audit_t *audit) { + GetHypercallInterface()->Abort(); +} + +void mcache_free(mcache_t *cp, void *buf) { + ((mcache_obj_t *)buf)->obj_next = NULL; + mcache_free_ext(cp, (mcache_obj_t *)buf); +} +void mcache_free_ext(mcache_t *cp, mcache_obj_t *list) { + bool is_mbuf = ((int)cp->mc_private == MC_MBUF_CL || + (int)cp->mc_private == MC_MBUF_BIGCL || + (int)cp->mc_private == MC_MBUF_16KCL); + if (cp->mc_slab_free && is_mbuf) { + cp->mc_slab_free(cp->mc_private, list, true); + return; + } + mcache_obj_t *tmp = NULL; + while (list) { + tmp = list->obj_next; + free(list); + list = tmp; + } +} + +unsigned int mcache_getflags(void) { + return 0; +} +void mcache_init() {} +boolean_t mcache_purge_cache(mcache_t *cp, boolean_t async) { + GetHypercallInterface()->Abort(); +} +void mcache_reap() { + GetHypercallInterface()->Abort(); +} +void mcache_reap_now(mcache_t *a1, boolean_t a2) { + GetHypercallInterface()->Abort(); +} +void mcache_set_pattern(u_int64_t a1, void *a2, size_t a3) { + GetHypercallInterface()->Abort(); +} +void mcache_waiter_dec(mcache_t *a1) { + GetHypercallInterface()->Abort(); +} +void mcache_waiter_inc(mcache_t *a1) { + GetHypercallInterface()->Abort(); +} diff --git a/fuzz/xnu/bsd/fakes/platform_expert.c b/fuzz/xnu/bsd/fakes/platform_expert.c new file mode 100644 index 0000000..6861d74 --- /dev/null +++ b/fuzz/xnu/bsd/fakes/platform_expert.c @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include + +static char fuzzer_model_name[] = "fuzzer_model"; +static char fuzzer_machine_name[] = "fuzzer_machine"; +static char fuzzer_target_name[] = "fuzzer_target_name"; +static char fuzzer_product_name[] = "fuzzer_product_name"; + +boolean_t PEGetModelName(char* name, int maxLength) { + strlcpy(name, fuzzer_model_name, maxLength); + return true; +} + +// third_party/xnu/BUILD/obj/EXPORT_HDRS/iokit/IOKit/IOPlatformExpert.h +boolean_t PEGetMachineName(char* name, int maxLength) { + strlcpy(name, fuzzer_machine_name, maxLength); + return true; +} + +int PEGetPlatformEpoch() { + return -1; +} + +int PEGetTargetName(char* name, int maxLength) { + strlcpy(name, fuzzer_target_name, maxLength); + return true; +} + +boolean_t PEGetProductName(char* name, int maxLength) { + strlcpy(name, fuzzer_product_name, maxLength); + return true; +} diff --git a/fuzz/xnu/bsd/fakes/root_device.c b/fuzz/xnu/bsd/fakes/root_device.c new file mode 100644 index 0000000..b08d893 --- /dev/null +++ b/fuzz/xnu/bsd/fakes/root_device.c @@ -0,0 +1,203 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/hypercall/hypercall.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVMAXNAMESIZE 32 +extern unsigned char rootdevice[DEVMAXNAMESIZE]; + +static int fuzzed_open(dev_t dev, int flags, int devtype, struct proc *p) { + return 0; +} + +static int fuzzed_close(dev_t dev, int flags, int devtype, struct proc *p) { + return 0; +} + +static void fuzzed_strategy(struct buf *bp) { + buf_biodone(bp); +} + +static int fuzzed_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) { + return 0; +} + +static int fuzzed_psize(dev_t dev) { + return 0; +} + +static int fuzzed_read(dev_t dev, struct uio *uio, int ioflag) { + return 0; +} + +static int fuzzed_write(dev_t dev, struct uio *uio, int ioflag) { + return 0; +} + +static const struct bdevsw fuzzed_bdevsw = { + .d_open = fuzzed_open, + .d_close = fuzzed_close, + .d_strategy = fuzzed_strategy, + .d_ioctl = fuzzed_ioctl, + .d_dump = eno_dump, + .d_psize = fuzzed_psize, + .d_type = D_DISK, +}; + +static const struct cdevsw fuzzed_cdevsw = { + .d_open = fuzzed_open, + .d_close = fuzzed_close, + .d_read = fuzzed_read, + .d_write = fuzzed_write, + .d_ioctl = fuzzed_ioctl, + .d_stop = eno_stop, + .d_reset = eno_reset, + .d_ttys = NULL, + .d_select = eno_select, + .d_mmap = eno_mmap, + .d_strategy = eno_strat, + .d_reserved_1 = eno_getc, + .d_reserved_2 = eno_putc, + .d_type = D_DISK, +}; + +static struct ifnet *fuzzed_ifp = NULL; + +static errno_t fuzzed_output(ifnet_t interface, mbuf_t m) { + // Handle packet output (for now just free the mbuf) + m_freem(m); + return 0; +} + +static errno_t fuzzed_demux(ifnet_t interface, mbuf_t m, char *header, protocol_family_t *protocol_family) { + // Handle packet demux (for now just return an error) + return EOPNOTSUPP; +} + +static errno_t fuzzed_add_proto(ifnet_t interface, protocol_family_t protocol_family, const struct ifnet_demux_desc *demux_list, u_int32_t demux_count) { + // Handle adding protocol (for now just return success) + return 0; +} + +static errno_t fuzzed_del_proto(ifnet_t interface, protocol_family_t protocol_family) { + // Handle deleting protocol (for now just return success) + return 0; +} + +static errno_t fuzzed_check_multi(ifnet_t interface, const struct sockaddr *multicast) { + // Handle multicast checking (for now just return success) + return 0; +} + +static errno_t fuzzed_ifnet_ioctl(ifnet_t interface, u_long cmd, void *data) { + struct ifreq *ifr = (struct ifreq *)data; + switch (cmd) { + case SIOCSIFFLAGS: + // Handle setting interface flags here + ifnet_set_flags(interface, IFF_UP | IFF_RUNNING, IFF_UP | IFF_RUNNING); + return 0; + default: + return EOPNOTSUPP; + } +} + +static void fuzzed_initialize(void) { + struct ifnet_init_params params; + + bzero(¶ms, sizeof(params)); + params.name = "fuzzed"; + params.unit = 0; + params.family = IFNET_FAMILY_ETHERNET; + params.type = IFT_ETHER; + params.output = fuzzed_output; + params.demux = fuzzed_demux; + params.add_proto = fuzzed_add_proto; + params.del_proto = fuzzed_del_proto; + params.check_multi = fuzzed_check_multi; + params.ioctl = fuzzed_ifnet_ioctl; + params.broadcast_addr = NULL; // Set to a valid broadcast address if needed + params.broadcast_len = 0; // Set to the correct length if needed + + if (ifnet_allocate(¶ms, &fuzzed_ifp) != 0) { + printf("Failed to allocate ifnet structure for fuzzed0\n"); + return; + } + + // Set additional parameters + ifnet_set_flags(fuzzed_ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0); + ifnet_set_baudrate(fuzzed_ifp, 0); // Set to the appropriate baudrate if known + + if (ifnet_attach(fuzzed_ifp, NULL) != 0) { + printf("Failed to attach fuzzed0 interface\n"); + ifnet_release(fuzzed_ifp); + fuzzed_ifp = NULL; + } else { + printf("Successfully initialized and attached fuzzed0 interface\n"); + } +} + +kern_return_t IOFindBSDRoot() { + // Initialize fuzzed interface + fuzzed_initialize(); + + // Add the fuzzed block device using bdevsw_add + int fuzzed_bmajor = bdevsw_add(-1, &fuzzed_bdevsw); + if (fuzzed_bmajor < 0) { + GetHypercallInterface()->ThreadPrintf("IOFindBSDRoot: bdevsw_add() returned %d\n", fuzzed_bmajor); + return KERN_FAILURE; + } + + // Add the fuzzed character device using cdevsw_add + int fuzzed_cmajor = cdevsw_add_with_bdev(-1, &fuzzed_cdevsw, fuzzed_bmajor); + if (fuzzed_cmajor < 0) { + GetHypercallInterface()->ThreadPrintf("IOFindBSDRoot: cdevsw_add() returned %d\n", fuzzed_cmajor); + return KERN_FAILURE; + } + + // Set rootdevice and rootdev to the created fuzzed device + int minor_number = 0; + int err = snprintf((char*)rootdevice, sizeof(rootdevice), "fuzzed%d", minor_number); + if (err < 0 || (size_t)err >= sizeof(rootdevice)) { + GetHypercallInterface()->ThreadPrintf("IOFindBSDRoot: snprintf() failed\n"); + return KERN_FAILURE; + } + rootdev = makedev(fuzzed_bmajor, minor_number); + + // Set flags, for example, 0 for a non-network device + u_int32_t flags = 0; + + return KERN_SUCCESS; +} diff --git a/fuzz/xnu/bsd/fakes/stubs.c b/fuzz/xnu/bsd/fakes/stubs.c new file mode 100644 index 0000000..ea9c18a --- /dev/null +++ b/fuzz/xnu/bsd/fakes/stubs.c @@ -0,0 +1,1429 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// Unimplemented stub functions +// These are placeholders that will be replaced with real or fake +// implementations when the fuzzed code attempts to use them. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" +#include "libkern/crypto/rand.h" +#include "netinet/dhcp_options.h" + +int printf(const char *format, ...); + +struct os_log_s { + int a; +}; + +struct os_log_s _os_log_default; + +// uint32_t crc32(uint32_t crc, const void *buf, size_t size) { +// assert(false); +// return 0; +// } + +uint64_t mem_actual = 0x41414141; + +// Fake registry entry structure +typedef struct { + char path[256]; + struct dhcp dhcp_response; + struct in_addr netmask; + struct in_addr router; +} fake_registry_entry_t; + +// Global variable to hold the fake registry entry +static fake_registry_entry_t fake_registry_entry; + +unsigned long simple_inet_addr(int a, int b, int c, int d) { + struct in_addr addr = {0}; + addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); + return addr.s_addr; +} + +void *IOBSDRegistryEntryForDeviceTree(char *path) { + if (strcmp(path, "/chosen") == 0) { + // Populate the fake registry entry with dummy data + fake_registry_entry.dhcp_response.dp_yiaddr.s_addr = + simple_inet_addr(192, 168, 1, 100); + fake_registry_entry.netmask.s_addr = simple_inet_addr(255, 255, 255, 0); + fake_registry_entry.router.s_addr = simple_inet_addr(192, 168, 1, 1); + + // Populate DHCP options + memset(fake_registry_entry.dhcp_response.dp_options, 0, + sizeof(fake_registry_entry.dhcp_response.dp_options)); + uint8_t *options = fake_registry_entry.dhcp_response.dp_options; + + // Magic cookie + options[0] = 99; + options[1] = 130; + options[2] = 83; + options[3] = 99; + + // Subnet mask option + options[4] = dhcptag_subnet_mask_e; + options[5] = 4; + memcpy(&options[6], &fake_registry_entry.netmask.s_addr, 4); + + // Router option + options[10] = dhcptag_router_e; + options[11] = 4; + memcpy(&options[12], &fake_registry_entry.router.s_addr, 4); + + // End option + options[16] = dhcptag_end_e; + + strncpy(fake_registry_entry.path, path, + sizeof(fake_registry_entry.path) - 1); + return &fake_registry_entry; + } + return NULL; +} + +void IOBSDRegistryEntryRelease(void *entry) { + // No action needed for the fake implementation + return; +} + +const void *IOBSDRegistryEntryGetData(void *entry, char *property_name, + int *packet_length) { + fake_registry_entry_t *regEntry = (fake_registry_entry_t *)entry; + + if (strcmp(property_name, "DHCP_RESPONSE") == 0 || + strcmp(property_name, "BOOTP_RESPONSE") == 0) { + *packet_length = sizeof(struct dhcp); + return ®Entry->dhcp_response; + } + return NULL; +} + +void IOBSDGetPlatformUUID() { + GetHypercallInterface()->Abort(); +} +void IOBSDMountChange() {} + +void IOBaseSystemARVRootHashAvailable() { + GetHypercallInterface()->Abort(); +} +void IOCatalogueMatchingDriversPresent() { + GetHypercallInterface()->Abort(); +} + +bool IOGetApfsPrebootUUID() { + return false; +} + +bool IOGetAssociatedApfsVolgroupUUID(char *uuid) { + return false; +} + +bool IOGetBootUUID() { + return false; +} + +bool IOGetVMMPresent() { + return GetHypercallInterface()->GetFuzzedBool(); +} + +kern_return_t IOKitBSDInit() { + return KERN_SUCCESS; +} +void IOKitInitializeTime() {} +void IOMapperInsertPage() { + GetHypercallInterface()->Abort(); +} +void IOPMCopySleepWakeUUIDKey() { + GetHypercallInterface()->Abort(); +} + +void IORegistrySetOSBuildVersion(char *build_version) {} + +void IOSecureBSDRoot() { + GetHypercallInterface()->Abort(); +} +void IOServicePublishResource() {} + +boolean_t IOServiceWaitForMatchingResource(const char *property, + uint64_t timeout) { + return true; +} + +void IOSetRecoveryBoot() { + GetHypercallInterface()->Abort(); +} +void IOSystemShutdownNotification() {} +void IOVnodeHasEntitlement() { + GetHypercallInterface()->Abort(); +} +void OSKextGetUUIDForName() { + GetHypercallInterface()->Abort(); +} +void OSKextResetAfterUserspaceReboot() { + GetHypercallInterface()->Abort(); +} +void PE_imgsrc_mount_supported() { + GetHypercallInterface()->Abort(); +} +void SHA1Final() { + GetHypercallInterface()->Abort(); +} +void SHA1Init() { + GetHypercallInterface()->Abort(); +} +void SHA1Update() { + GetHypercallInterface()->Abort(); +} +void SecureDTGetProperty() { + GetHypercallInterface()->Abort(); +} + +int SecureDTLookupEntry() { + return -1; +} + +void _os_log_internal_driverKit() { + GetHypercallInterface()->Abort(); +} +void act_thread_catt() { + GetHypercallInterface()->Abort(); +} +void *act_thread_csave() { + return NULL; +} +void aes_decrypt_aad_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_cbc() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_finalize_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_get_ctx_size_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_key() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_key_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_decrypt_set_iv_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_aad_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_cbc() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_finalize_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_get_ctx_size_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_inc_iv_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_key() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_key128() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_key_with_iv_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_reset_gcm() { + GetHypercallInterface()->Abort(); +} + +struct msgbuf *aslbufp; + +void audit_analytics() { + GetHypercallInterface()->Abort(); +} +void audit_triggers() { + GetHypercallInterface()->Abort(); +} +void bootsessionuuid_string() { + GetHypercallInterface()->Abort(); +} +int bsd_do_post() { + return 0; +} +int bsd_list_tests() { + return 0; +} +void bzero_phys() { + GetHypercallInterface()->Abort(); +} +void bzero_phys_nc() { + GetHypercallInterface()->Abort(); +} +void check_task_access_with_flavor() { + GetHypercallInterface()->Abort(); +} +void commpage_update_dyld_flags() { + GetHypercallInterface()->Abort(); +} +void commpage_update_kdebug_state() { + GetHypercallInterface()->Abort(); +} +int copyin_atomic32_wait_if_equals(const user_addr_t user_addr, uint32_t u32) { + GetHypercallInterface()->Abort(); +} +void copypv() { + GetHypercallInterface()->Abort(); +} +void copysize_limit_panic() { + GetHypercallInterface()->Abort(); +} + +void coredumpok() { + GetHypercallInterface()->Abort(); +} +cpu_type_t cpu_type() { + return CPU_TYPE_I386; +} + +uint32_t cpuid_cpufamily(void) { + return CPU_TYPE_I386; +} + +void cpuid_extfeatures() { + GetHypercallInterface()->Abort(); +} + +void *cpuid_info() { + return NULL; +} + +// Dummy implementation of di_root_image +int di_root_image(const char *root_path, char *rootdevice, int max_length, + dev_t *dev) { + // Don't update rootdev as we already initialized it properly in our fake `IOFindBSDRoot` + *dev = rootdev; + + strncpy(rootdevice, "dummy_root_device", max_length - 1); + rootdevice[max_length - 1] = '\0'; + + return 0; +} + +void di_root_image_ext() { + GetHypercallInterface()->Abort(); +} +void di_root_ramfile_buf() { + GetHypercallInterface()->Abort(); +} +void early_boot() { + GetHypercallInterface()->Abort(); +} +void find_code_signature() { + GetHypercallInterface()->Abort(); +} + +// From loose_ends.c +int fls(unsigned int mask) { + if (mask == 0) { + return 0; + } + + return (sizeof(mask) << 3) - __builtin_clz(mask); +} + +void gPEClockFrequencyInfo() { + GetHypercallInterface()->Abort(); +} +void get_largest_zone_info() {} +void get_zone_map_size() {} +void halt_log_enter() {} +void hashaddr() { + GetHypercallInterface()->Abort(); +} +void hashbacktrace() { + GetHypercallInterface()->Abort(); +} +void high_MutexSpin() { + GetHypercallInterface()->Abort(); +} + +// Dummy implementation of kdp_get_interface +void *kdp_get_interface() { + // Return a non-null pointer to simulate a valid interface + static int dummy_interface; + return &dummy_interface; +} + +// Dummy implementation of kdp_set_ip_and_mac_addresses +void kdp_set_ip_and_mac_addresses(struct in_addr *ip_addr, void *mac_addr) { + // No action needed for the fake implementation +} + +// Dummy implementation of kdp_set_gateway_mac +void kdp_set_gateway_mac(void *mac_addr) { + // No action needed for the fake implementation +} + +void kpc_arch_init() {} + +uint32_t kpc_get_classes() { + return 0; +} + +int kpc_get_pmu_version() { + return GetHypercallInterface()->GetFuzzedUint32(); +} + +void kpc_set_sw_inc() {} +void kperf_disable_sampling() { + GetHypercallInterface()->Abort(); +} +void kperf_reset() { + GetHypercallInterface()->Abort(); +} +void lockd_request() { + GetHypercallInterface()->Abort(); +} +void lockd_shutdown() { + GetHypercallInterface()->Abort(); +} + +int log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t *retval) { + return 0; +} + +void log_putc_locked(struct msgbuf *a, char b) { + GetHypercallInterface()->Abort(); +} + +void log_setsize() { + GetHypercallInterface()->Abort(); +} +void logwakeup() { + GetHypercallInterface()->Abort(); +} +void low_MutexSpin() { + GetHypercallInterface()->Abort(); +} +uint64_t mach_bridge_remote_time() { + return 0; +} +void mach_bridge_timer_enable() { + GetHypercallInterface()->Abort(); +} +void mach_gss_accept_sec_context() { + GetHypercallInterface()->Abort(); +} +void mach_gss_init_sec_context_v3() { + GetHypercallInterface()->Abort(); +} +void mach_gss_log_error() { + GetHypercallInterface()->Abort(); +} +void mach_gss_lookup() { + GetHypercallInterface()->Abort(); +} + +kern_return_t machine_thread_function_pointers_convert_from_user( + thread_t thread, user_addr_t *fptrs, uint32_t count) { + return KERN_SUCCESS; +} + +void machine_thread_on_core() { + GetHypercallInterface()->Abort(); +} +void max_ncpus() { + GetHypercallInterface()->Abort(); +} +void ml_cpu_cache_sharing() { + GetHypercallInterface()->Abort(); +} +void ml_cpu_cache_size() { + GetHypercallInterface()->Abort(); +} +void rsa_make_pub() { + GetHypercallInterface()->Abort(); +} +void rsa_verify_pkcs1v15() { + GetHypercallInterface()->Abort(); +} +// void __muloti4() { +// GetHypercallInterface()->Abort(); +// } +void _get_cpu_capabilities() {} +void nx_enabled() { + GetHypercallInterface()->Abort(); +} +void oslog_setsize() { + GetHypercallInterface()->Abort(); +} + +void osobject_release(void *object) { + GetHypercallInterface()->Abort(); +} + +void osobject_retain(void *object) { + GetHypercallInterface()->Abort(); +} + +char osversion[256] = "SockFuzzer OS"; + +void panic_dump_mem() { + GetHypercallInterface()->Abort(); +} +void panic_hook() {} +void panic_phys_range_before() { + GetHypercallInterface()->Abort(); +} +void panic_unhook() {} +void registerSleepWakeInterest() { + GetHypercallInterface()->Abort(); +} +void send_nspace_resolve_cancel() { + GetHypercallInterface()->Abort(); +} + +void send_nspace_resolve_path() { + GetHypercallInterface()->Abort(); +} + +void startup_serial_num_procs() { + GetHypercallInterface()->Abort(); +} +void test_oslog_handleOSLogCtl() { + GetHypercallInterface()->Abort(); +} +user_addr_t thread_adjuserstack(thread_t thread, int adjust) { + return 0; +} +kern_return_t thread_entrypoint(__unused thread_t thread, int flavor, + thread_state_t tstate, + __unused unsigned int count, + mach_vm_offset_t *entry_point) { + *entry_point = CAST_USER_ADDR_T(VM_MIN_ADDRESS); + return KERN_SUCCESS; +} +void thread_set_child() {} +void thread_set_parent() { + GetHypercallInterface()->Abort(); +} + +void thread_setentrypoint(thread_t thread, mach_vm_offset_t entry) { + GetHypercallInterface()->Abort(); +} + +kern_return_t thread_setsinglestep(thread_t thread, int on) { + GetHypercallInterface()->Abort(); +} + +void thread_setuserstack(thread_t thread, mach_vm_offset_t user_stack) { + GetHypercallInterface()->Abort(); +} +kern_return_t thread_userstack(__unused thread_t thread, int flavor, + thread_state_t tstate, unsigned int count, + mach_vm_offset_t *user_stack, int *customstack, + __unused boolean_t is64bit) { + *user_stack = GetHypercallInterface()->GetFuzzedUint64(); + if (customstack) { + *customstack = GetHypercallInterface()->GetFuzzedBool(); + } + return KERN_SUCCESS; +} +kern_return_t thread_userstackdefault(mach_vm_offset_t *default_user_stack, + boolean_t is64bit __unused) { + *default_user_stack = GetHypercallInterface()->GetFuzzedUint64(); + return KERN_SUCCESS; +} +void tsc_at_boot() { + GetHypercallInterface()->Abort(); +} +void tsc_rebase_abs_time() { + GetHypercallInterface()->Abort(); +} +void uuid_generate(uuid_t out) { + GetHypercallInterface()->Abort(); +} +int uuid_parse(const uuid_string_t in, uuid_t uu) { + GetHypercallInterface()->Abort(); +} +void xnupost_export_testdata() { + GetHypercallInterface()->Abort(); +} +void xnupost_get_estimated_testdata_size() { + GetHypercallInterface()->Abort(); +} +void xnupost_reset_all_tests() { + GetHypercallInterface()->Abort(); +} +void zdestroy(zone_t zone) { + GetHypercallInterface()->Abort(); +} +void zone_gc_drain() { + GetHypercallInterface()->Abort(); +} + +void OSReportWithBacktrace() { + GetHypercallInterface()->Abort(); +} +void _Block_object_assign() { + GetHypercallInterface()->Abort(); +} +void _NSConcreteGlobalBlock() { + GetHypercallInterface()->Abort(); +} +void compute_pmap_gc_throttle() { + GetHypercallInterface()->Abort(); +} +void compute_zone_working_set_size() { + GetHypercallInterface()->Abort(); +} +void driverkit_checkin_timed_out() { + GetHypercallInterface()->Abort(); +} +void iokit_user_client_trap() {} +void kperf_timer_expire() { + GetHypercallInterface()->Abort(); +} +void mp_slave_stack() { + GetHypercallInterface()->Abort(); +} +void osbuild_config() { + GetHypercallInterface()->Abort(); +} +void oslog_msgbuf_dropped_charcount() { + GetHypercallInterface()->Abort(); +} +void oslog_msgbuf_dropped_msgcount() { + GetHypercallInterface()->Abort(); +} +void oslog_msgbuf_msgcount() { + GetHypercallInterface()->Abort(); +} +void osrelease() { + GetHypercallInterface()->Abort(); +} +void ostype() { + GetHypercallInterface()->Abort(); +} +kern_return_t thread_set_wq_state32(thread_t thread, thread_state_t tstate) { + GetHypercallInterface()->Abort(); +} +kern_return_t thread_set_wq_state64(thread_t thread, thread_state_t tstate) { + GetHypercallInterface()->Abort(); +} +void gEnforceQuiesceSafety() { + GetHypercallInterface()->Abort(); +} +void iotrace_generators() { + GetHypercallInterface()->Abort(); +} +void iotrace_next() { + GetHypercallInterface()->Abort(); +} +void iotrace_ring() { + GetHypercallInterface()->Abort(); +} +void reportphyreadosbt() { + GetHypercallInterface()->Abort(); +} +void reportphywriteosbt() { + GetHypercallInterface()->Abort(); +} +void tracephyreaddelayabs() { + GetHypercallInterface()->Abort(); +} +void tracephywritedelayabs() { + GetHypercallInterface()->Abort(); +} + +void lck_rw_set_promotion_locked() { + GetHypercallInterface()->Abort(); +} +bool kperf_is_sampling(void) { + return false; +} +vm_offset_t other_percpu_base(int cpu) { + return 0; +} +void sysctl_get_bound_cpuid() { + GetHypercallInterface()->Abort(); +} +void sysctl_task_get_no_smt() { + GetHypercallInterface()->Abort(); +} +void sysctl_task_set_no_smt() { + GetHypercallInterface()->Abort(); +} +void sysctl_thread_bind_cpuid() { + GetHypercallInterface()->Abort(); +} + +void OSKextResetPgoCounters() { + GetHypercallInterface()->Abort(); +} +kern_return_t OSKextGrabPgoData() { + return KERN_FAILURE; +} +void OSKextResetPgoCountersLock() {} +void OSKextResetPgoCountersUnlock() {} + +void pid_hibernate() {} +int debug_syscall_reject() { + return ENOTSUP; +} + +void logclose() { + GetHypercallInterface()->Abort(); +} +void logread() { + GetHypercallInterface()->Abort(); +} +void logioctl() { + GetHypercallInterface()->Abort(); +} +void logselect() { + GetHypercallInterface()->Abort(); +} +void logopen() { + GetHypercallInterface()->Abort(); +} +void _kmem_alloc_pageable() { + GetHypercallInterface()->Abort(); +} + +const img4_interface_t *img4if = NULL; + +const amfi_t *amfi = NULL; + +void _kmem_alloc() { + GetHypercallInterface()->Abort(); +} +void _vm_fault() { + GetHypercallInterface()->Abort(); +} +void bt_params_get_latest() { + GetHypercallInterface()->Abort(); +} +kern_return_t kdp_core_handle_new_encryption_key( + IOCoreFileAccessCallback access_data, void *access_context, + void *recipient_context) { + GetHypercallInterface()->Abort(); +} +kern_return_t IOProvideCoreFileAccess(IOCoreFileAccessRecipient recipient, + void *recipient_context) { + return KERN_SUCCESS; +} +void _vm_kernel_addrhash() { + GetHypercallInterface()->Abort(); +} +void io_compression_stats() { + GetHypercallInterface()->Abort(); +} + +void _kmem_alloc_kobject() { + GetHypercallInterface()->Abort(); +} +void clock_timebase_info(mach_timebase_info_data_t *info) { + info->denom = 1; + info->numer = 1; +} +void vnode_iocs_record_and_free() { + GetHypercallInterface()->Abort(); +} +void clock_alarm_reply() { + GetHypercallInterface()->Abort(); +} +void machine_signal_idle_cancel() { + GetHypercallInterface()->Abort(); +} +void send_port_space_violation() { + GetHypercallInterface()->Abort(); +} +void send_file_descriptors_violation() { + GetHypercallInterface()->Abort(); +} + +void iokit_client_died() { + GetHypercallInterface()->Abort(); +} +void iokit_port_for_object() { + GetHypercallInterface()->Abort(); +} +void pmap_map_block() { + GetHypercallInterface()->Abort(); +} +void pmap_memory_region_count() { + GetHypercallInterface()->Abort(); +} +void pmap_memory_regions() { + GetHypercallInterface()->Abort(); +} +int PE_init_socd_client() { + return 0; +} +void PE_write_socd_client_buffer() { + GetHypercallInterface()->Abort(); +} +void ml_get_timebase_entropy() { + GetHypercallInterface()->Abort(); +} + +void sdt_probetab() { + GetHypercallInterface()->Abort(); +} +void sdt_probetab_mask() { + GetHypercallInterface()->Abort(); +} +void dtrace_probe() { + GetHypercallInterface()->Abort(); +} +void dtrace_invop_callsite_pre() { + GetHypercallInterface()->Abort(); +} +void dtrace_invop_callsite_post() { + GetHypercallInterface()->Abort(); +} +void cpu_core() { + GetHypercallInterface()->Abort(); +} +void cpu_list() { + GetHypercallInterface()->Abort(); +} + +uint64_t cpuid_features() { + return 0; +} + +char *cpuid_get_feature_names(uint64_t features, char *buf, unsigned buf_len) { + memset(buf, 0, buf_len); + return NULL; +} + +void cpuid_get_leaf7_feature_names() { + GetHypercallInterface()->Abort(); +} +void cpuid_get_leaf7_extfeature_names() { + GetHypercallInterface()->Abort(); +} +void cpuid_get_extfeature_names() { + GetHypercallInterface()->Abort(); +} +void flex_ratio() { + GetHypercallInterface()->Abort(); +} +void flex_ratio_min() { + GetHypercallInterface()->Abort(); +} +void flex_ratio_max() { + GetHypercallInterface()->Abort(); +} +void ucode_interface() { + GetHypercallInterface()->Abort(); +} +void panic_restart_timeout() { + GetHypercallInterface()->Abort(); +} +void interrupt_populate_latency_stats() { + GetHypercallInterface()->Abort(); +} +void interrupt_reset_latency_stats() { + GetHypercallInterface()->Abort(); +} +void NMI_count() { + GetHypercallInterface()->Abort(); +} +void NMI_cpus() { + GetHypercallInterface()->Abort(); +} +bool cpuid_vmm_present() { + return false; +} +void cpuid_vmm_get_applepv_features() { + GetHypercallInterface()->Abort(); +} +void hvg_hcall_trigger_dump() { + GetHypercallInterface()->Abort(); +} +void pv_hashed_kern_low_water_mark() { + GetHypercallInterface()->Abort(); +} +void tscFreq() { + GetHypercallInterface()->Abort(); +} +void deep_idle_rebase() { + GetHypercallInterface()->Abort(); +} +void pal_rtc_nanotime_info() { + GetHypercallInterface()->Abort(); +} +void is_x2apic() { + GetHypercallInterface()->Abort(); +} + +uint32_t interrupt_timer_coalescing_enabled; + +void idle_entry_timer_processing_hdeadline_threshold() { + GetHypercallInterface()->Abort(); +} + +uint32_t ml_timer_eager_evaluations = 0; + +void ml_timer_eager_evaluation_max() { + GetHypercallInterface()->Abort(); +} +void x86_isr_fp_simd_use() { + GetHypercallInterface()->Abort(); +} +void ml_cpu_power_disable(__unused int cpu_id) {} +void ml_cpu_power_enable(__unused int cpu_id) {} + +int sys_debug_syscall_reject_config() { + return 0; +} + +void hvg_hcall_get_mabs_offset() { + GetHypercallInterface()->Abort(); +} + +void hvg_hcall_get_bootsessionuuid() { + GetHypercallInterface()->Abort(); +} + +void wake_nkdbufs() { + GetHypercallInterface()->Abort(); +} + +void trace_wrap() { + GetHypercallInterface()->Abort(); +} + +void IOCurrentTaskGetEntitlement() { + GetHypercallInterface()->Abort(); +} + +void IOVnodeGetEntitlement() { + GetHypercallInterface()->Abort(); +} + +void kern_register_userspace_coredump() { + GetHypercallInterface()->Abort(); +} + +kern_return_t machine_task_process_signature() { + return KERN_SUCCESS; +} + +void ktriage_extract() { + GetHypercallInterface()->Abort(); +} + +void IOUserServerRecordExitReason() { + GetHypercallInterface()->Abort(); +} + +void ml_get_cluster_type_name() { + GetHypercallInterface()->Abort(); +} + +void IOParseWorkloadConfig() { + GetHypercallInterface()->Abort(); +} + +void reset_debug_syscall_rejection_mode() { + GetHypercallInterface()->Abort(); +} + +void IOCurrentTaskHasStringEntitlement() { + GetHypercallInterface()->Abort(); +} + +void enable_dklog_serial_output() { + GetHypercallInterface()->Abort(); +} + +void record_system_event_no_varargs() {} + +void os_memcmp_mask_16B() { + GetHypercallInterface()->Abort(); +} + +void os_memcmp_mask_32B() { + GetHypercallInterface()->Abort(); +} + +void ktriage_record() { + // GetHypercallInterface()->Abort(); +} + +void send_vfs_resolve_dir_with_audit_token() { + GetHypercallInterface()->Abort(); +} + +void send_vfs_resolve_file_with_audit_token() { + GetHypercallInterface()->Abort(); +} + +void ml_io_map_unmappable() { + GetHypercallInterface()->Abort(); +} + +void current_space_size() { + GetHypercallInterface()->Abort(); +} + +void write_trace_on_panic() { + GetHypercallInterface()->Abort(); +} + +void kdp_polled_corefile_mode() { + GetHypercallInterface()->Abort(); +} + +void mach_exception_raise_backtrace() { + GetHypercallInterface()->Abort(); +} + +wait_result_t cond_sleep_with_inheritor64_mask(cond_swi_var_t cond, + cond_swi_var64_s expected_cond, + uint64_t check_mask, + wait_interrupt_t interruptible, + uint64_t deadline) { + GetHypercallInterface()->Abort(); +} + +kern_return_t cond_wakeup_all_with_inheritor(cond_swi_var_t cond, + wait_result_t result) { + GetHypercallInterface()->Abort(); +} + +void kdp_memcpy() { + GetHypercallInterface()->Abort(); +} + +void kdp_strlcpy() { + GetHypercallInterface()->Abort(); +} + +void kdp_generic_copyin() { + GetHypercallInterface()->Abort(); +} + +void kdp_find_phys() { + GetHypercallInterface()->Abort(); +} + +void kdp_generic_copyin_word() { + GetHypercallInterface()->Abort(); +} + +void kdp_generic_copyin_string() { + GetHypercallInterface()->Abort(); +} + +void kdp_vm_map_get_page_size() { + GetHypercallInterface()->Abort(); +} + +void ml_cpu_down_update_counts() { + GetHypercallInterface()->Abort(); +} + +void gEnforcePlatformActionSafety() { + GetHypercallInterface()->Abort(); +} + +// TODO(nedwill) +void kern_unregister_userspace_coredump() {} + +void machine_thread_process_signature() {} + +bool crypto_init = false; + +bool hvg_is_hcall_available() { + return false; +} + +void kdebug_startup() {} + +bool os_log_disabled(void) { + return false; +} + +void dtrace_init() {} + +void helper_init() {} + +void lockstat_init() {} + +void lockprof_init() {} + +void sdt_init() {} + +void systrace_init() {} + +void fbt_init() {} + +void profile_init() {} + +void oslogopen() { + GetHypercallInterface()->Abort(); +} + +void oslogclose() { + GetHypercallInterface()->Abort(); +} + +void oslogioctl() { + GetHypercallInterface()->Abort(); +} + +void oslogselect() { + GetHypercallInterface()->Abort(); +} + +void pmap_query_page_info_retries() { + GetHypercallInterface()->Abort(); +} + +void grab_pgo_data() {} + +void do_pgo_reset_counters() { + GetHypercallInterface()->Abort(); +} + +void hw_spin_should_keep_spinning() { + GetHypercallInterface()->Abort(); +} + +EVENT_DEFINE(ZONE_EXHAUSTED); + +void hw_lck_ticket_lock() { + GetHypercallInterface()->Abort(); +} + +void zone_get_stats(zone_t zone, struct zone_basic_stats *stats) { + GetHypercallInterface()->Abort(); +} + +void lck_ticket_unlock_nopreempt(lck_ticket_t *tlock) { + GetHypercallInterface()->Abort(); +} + +void zone_drain(zone_t zone) { + GetHypercallInterface()->Abort(); +} + +void hw_lck_ticket_unlock_nopreempt() { + GetHypercallInterface()->Abort(); +} + +void hw_spin_compute_timeout() { + GetHypercallInterface()->Abort(); +} + +void percpu_slot_lck_mcs() { + GetHypercallInterface()->Abort(); +} + +void lck_ticket_lock_nopreempt(lck_ticket_t *tlock, lck_grp_t *grp) { + GetHypercallInterface()->Abort(); +} + +void zcache_drain(zone_id_t zone_id) { + GetHypercallInterface()->Abort(); +} + +void _zc_mag_size() { + GetHypercallInterface()->Abort(); +} + +void hw_lck_ticket_lock_nopreempt() { + GetHypercallInterface()->Abort(); +} + +void zone_enable_smr(zone_t zone, struct smr *smr, zone_smr_free_cb_t free_cb) { + /* + * Scalable Memory Reclamation (SMR) is not supported in the sockfuzzer environment. + * + * SMR allows for efficient memory reclamation in multi-threaded + * environments by using sequence numbers to track freed elements and + * allowing them to be reused without explicit synchronization. + * + * We need to revisit SMR support in the future, especially with regards + * to security and safety. Some potential risks include: + * + * - Sequence number management: If sequence numbers are not managed + * correctly, it could lead to freed elements being reused prematurely, + * resulting in use-after-free vulnerabilities. + * - ABA problem: The ABA problem could occur if a sequence number wraps + * around before a freed element is reused. This could lead to a thread + * mistakenly believing that an element is safe to reuse when it is not. + * - Memory ordering: Ensuring correct memory ordering is crucial for + * SMR to function correctly. Without proper memory barriers, threads + * could observe stale sequence numbers or freed elements. + */ +} + +#undef zfree_id_smr +void zfree_id_smr(zone_id_t zone_id, void *addr __unsafe_indexable) { + GetHypercallInterface()->Abort(); +} + +#undef zfree_smr +void zfree_smr(zone_t zone, void *elem __unsafe_indexable) { + GetHypercallInterface()->Abort(); +} + +boolean_t PEReadNVRAMBooleanProperty(const char *symbol, boolean_t *value) { + *value = GetHypercallInterface()->GetFuzzedBool(); + return true; +} + +void lck_rw_lock_count_dec() { + GetHypercallInterface()->Abort(); +} + +void _bcopy() { + GetHypercallInterface()->Abort(); +} + +char *__null_terminated strnstr(const char *__null_terminated s, + const char *__null_terminated find, + size_t slen) { + GetHypercallInterface()->Abort(); +} + +void crypto_random_kmem_init(crypto_random_ctx_t ctx) {} + +void lck_rw_lock_count_inc() { + GetHypercallInterface()->Abort(); +} + +void PERemoveNVRAMProperty() { + GetHypercallInterface()->Abort(); +} + +void PEWriteNVRAMBooleanProperty() { + GetHypercallInterface()->Abort(); +} + +void pmap_user_va_bits() { + GetHypercallInterface()->Abort(); +} + +void crypto_random_uniform(crypto_random_ctx_t ctx, uint64_t bound, + uint64_t *random) { + if (bound == 0) { + *random = 0; // Handle the edge case where bound is 0. + return; + } + + // Calculate the number of bits needed to represent the bound. + uint64_t bits = 64 - __builtin_clzll(bound - 1); + + if (bits <= 32) { + *random = GetHypercallInterface()->GetFuzzedUint32InRange(0, bound - 1); + } else { + // Generate a 64-bit random value and mask off the upper bits + // to ensure it falls within the desired range. + *random = GetHypercallInterface()->GetFuzzedUint64() % bound; + } +} + +bool +pmap_get_tpro( + __unused pmap_t pmap) +{ + return false; +} + +void ml_addr_in_non_xnu_stack() { + GetHypercallInterface()->Abort(); +} + +void pmap_set_tpro() { + GetHypercallInterface()->Abort(); +} + +void ml_did_interrupt_userspace() { + GetHypercallInterface()->Abort(); +} + +void iokit_kobject_retain() { + GetHypercallInterface()->Abort(); +} + +void send_vfs_resolve_reparent_with_audit_token() { + GetHypercallInterface()->Abort(); +} + +void ml_page_protection_type() { + GetHypercallInterface()->Abort(); +} + +// The following two functions are for use in the kmem subsystem +// only. They are NOT guaranteed to provide cryptographic randomness +// and should not be used elsewhere. + +// Return the size needed for a random generator to be used by +// kmem. (See the discussion below for the semantics of this +// generator.) +// +// The returned value may vary by platform, but it is guaranteed to be +// no larger than CRYPTO_RANDOM_MAX_CTX_SIZE. +size_t crypto_random_kmem_ctx_size(void) { + return CRYPTO_RANDOM_MAX_CTX_SIZE; +} + +void iokit_copy_object_for_consumed_kobject() { + GetHypercallInterface()->Abort(); +} + +void lock_panic_timeout() { + GetHypercallInterface()->Abort(); +} + +// TODO(nedwill): support ktriage? +void ktriage_register_subsystem_strings() {} + +void xnu_strncmp() { + GetHypercallInterface()->Abort(); +} + +void xnu_strncpy() { + GetHypercallInterface()->Abort(); +} + +void xnu_strncasecmp() { + GetHypercallInterface()->Abort(); +} + +vm_offset_t ml_static_unslide(vm_offset_t vaddr) { + GetHypercallInterface()->Abort(); +} + +void telemetry_backtrace_add_kexts() { + GetHypercallInterface()->Abort(); +} +void ml_fp_save_area_prealloc() { + GetHypercallInterface()->Abort(); +} +void kpc_set_period() { + GetHypercallInterface()->Abort(); +} +void mt_microstackshot_ctr() { + GetHypercallInterface()->Abort(); +} + +vm_offset_t vm_kernel_etext; + +void IOTaskHasStringEntitlement() { + GetHypercallInterface()->Abort(); +} +void kperf_thread_ast_handler() { + GetHypercallInterface()->Abort(); +} +void kpc_get_actionid() { + GetHypercallInterface()->Abort(); +} +void kpc_get_counter_count() { + GetHypercallInterface()->Abort(); +} +void kpc_supported() { + GetHypercallInterface()->Abort(); +} +void mt_microstackshot_period() { + GetHypercallInterface()->Abort(); +} +void kpc_set_actionid() { + GetHypercallInterface()->Abort(); +} +void kpc_set_config_kernel() { + GetHypercallInterface()->Abort(); +} +void kpc_get_config_count() { + GetHypercallInterface()->Abort(); +} +void kpc_counterbuf_free() { + GetHypercallInterface()->Abort(); +} +void kpc_get_period() { + GetHypercallInterface()->Abort(); +} + +void mt_dev_init() {} + +void kpc_get_counterbuf_size() { + GetHypercallInterface()->Abort(); +} +void image4_dlxk_get() { + GetHypercallInterface()->Abort(); +} +void IOTaskGetEntitlement() { + GetHypercallInterface()->Abort(); +} +void usimple_lock_assert() { + GetHypercallInterface()->Abort(); +} +void kpc_counterbuf_alloc() { + GetHypercallInterface()->Abort(); +} +void _memmove() { + GetHypercallInterface()->Abort(); +} diff --git a/fuzz/xnu/bsd/fakes/vfs.c b/fuzz/xnu/bsd/fakes/vfs.c new file mode 100644 index 0000000..c3a9faa --- /dev/null +++ b/fuzz/xnu/bsd/fakes/vfs.c @@ -0,0 +1,313 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +struct mount; + +void abort(); + +#define VOPFUNC int (*)(void *) +int (**fuzzed_vnodeop_p)(void *); + +int fuzzed_mount(struct mount *mp, vnode_t devvp, user_addr_t data, + vfs_context_t context) { + return 0; +} + +int fuzzed_start(struct mount *mp, int flags, vfs_context_t context) { + return 0; +} + +int fuzzed_unmount(struct mount *mp, int mntflags, vfs_context_t context) { + return 0; +} + +int fuzzed_root(struct mount *mp, struct vnode **vpp, vfs_context_t context) { + if (!vpp) { + return EINVAL; + } + struct vnode_fsparam vnfs_param = { + .vnfs_mp = mp, + // TODO(nedwill): fuzz this with VDIR, VBLK, etc. + .vnfs_str = "fuzzed_vnfs", + .vnfs_vtype = VREG, + .vnfs_dvp = NULL, + .vnfs_fsnode = (void *)1, + .vnfs_vops = fuzzed_vnodeop_p, + .vnfs_markroot = true, + .vnfs_marksystem = false, + .vnfs_rdev = 0, + // TODO(nedwill): can't fuzz during boot stage + .vnfs_filesize = + 1024, // GetHypercallInterface()->GetFuzzedUint32InRange(0, 1024), + // TODO(nedwill) + .vnfs_cnp = NULL, + .vnfs_flags = VNFS_CANTCACHE | VNFS_NOCACHE}; + + return vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vnfs_param, vpp); +} + +int fuzzed_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, + vfs_context_t context) { + // TODO(nedwill): support this + return 0; +} + +int fuzzed_getattr(struct mount *mp, struct vfs_attr *attr, + vfs_context_t context) { + // GetHypercallInterface()->GetFuzzedBytes((uint8_t*)attr, sizeof(struct vfs_attr)); + return 0; +} + +int fuzzed_sync(struct mount *mp, int waitfor, vfs_context_t context) { + return 0; +} + +int fuzzed_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, + vfs_context_t context) { + abort(); + return 0; +} +int fuzzed_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, + struct vnode **vpp, vfs_context_t context) { + abort(); + return 0; +} +int fuzzed_vptofh(struct vnode *vp, int *fhlen, unsigned char *fhp, + vfs_context_t context) { + abort(); + return 0; +} +int fuzzed_init(struct vfsconf *conf) { + return 0; +} +int fuzzed_sysctl(int *a1, u_int a2, user_addr_t a3, size_t *a4, user_addr_t a5, + size_t a6, vfs_context_t context) { + return 0; +} +int fuzzed_setattr(struct mount *mp, struct vfs_attr *a2, + vfs_context_t context) { + abort(); + return 0; +} +int fuzzed_ioctl(struct mount *mp, u_long command, caddr_t data, int flags, + vfs_context_t context) { + return 0; +} +int fuzzed_vget_snapdir(struct mount *mp, struct vnode **vpp, + vfs_context_t context) { + return 1; +} + +int fuzzed_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx) { + return 0; +} + +struct vfsops fuzzed_vfsops = { + .vfs_mount = fuzzed_mount, + .vfs_start = fuzzed_start, + .vfs_unmount = fuzzed_unmount, + .vfs_root = fuzzed_root, + .vfs_quotactl = fuzzed_quotactl, + .vfs_getattr = fuzzed_getattr, + .vfs_sync = fuzzed_sync, + .vfs_vget = fuzzed_vget, + .vfs_fhtovp = fuzzed_fhtovp, + .vfs_vptofh = fuzzed_vptofh, + .vfs_init = fuzzed_init, + .vfs_sysctl = fuzzed_sysctl, + .vfs_setattr = fuzzed_setattr, + .vfs_ioctl = fuzzed_ioctl, + .vfs_vget_snapdir = fuzzed_vget_snapdir, +}; + +int unsupported_function(void *ptr) { + abort(); + return ENOTSUP; +} + +int nop_function(void *ptr) { + return ENOTSUP; +} + +int fuzzed_pagein(struct vnop_pagein_args *args) { + // TODO(nedwill): fill in data here? + return GetHypercallInterface()->GetFuzzedBool(); +} + +int fuzzed_vnop_getattr(struct vnop_getattr_args *args) { + // TODO(nedwill): fuzz with grammar + // GetHypercallInterface()->GetFuzzedBytes((uint8_t *)args->a_vap, sizeof(*args->a_vap)); + // TODO(nedwill): let the fuzzer discover these flags + args->a_vap->va_supported = 0xFFFFFFFFFFFFFFFF; + args->a_vap->va_type = VREG; + args->a_vap->va_mode = S_IXUSR | S_IXGRP | S_IXOTH; + args->a_vap->va_acl = NULL; + // printf("setting vap->va_data_size (%p, %d)\n", args->a_vap, 1024); + args->a_vap->va_data_size = + 1024; // GetHypercallInterface()->GetFuzzedUint32InRange(0, 1024); + return 0; +} + +int fuzzed_pathconf(struct vnop_pathconf_args *ap) { + *ap->a_retval = + GetHypercallInterface()->GetFuzzedUint32InRange(0, UINT32_MAX); + return 0; +} + +int fuzzed_open(struct vnop_open_args *args) { + return 0; +} + +int fuzzed_close(struct vnop_close_args *args) { + return 0; +} + +int fuzzed_mmap_check(struct vnop_mmap_check_args *args) { + return 0; +} + +int fuzzed_fsync(struct vnop_fsync_args *args) { + return 0; +} + +void *malloc(size_t size); + +int fuzzed_read(struct vnop_read_args *args) { + uint32_t length = + 1024; // GetHypercallInterface()->GetFuzzedUint32InRange(0, 1024); + uint8_t *buffer = malloc(length); + if (!buffer) { + length = 0; + } + GetHypercallInterface()->GetFuzzedBytes(buffer, length); + GetHypercallInterface()->BlockCopyin(); + uiomove((char *)buffer, length, args->a_uio); + GetHypercallInterface()->UnblockCopyin(); + return 0; +} + +errno_t fuzzed_vnop_inactive(struct vnop_inactive_args *ap) { + vnode_t vp = ap->a_vp; + vfs_context_t ctx = ap->a_context; + + // Check if the vnode is already marked for recycle + if (vnode_isrecycled(vp)) { + printf("Vnode %p is already marked for recycle\n", vp); + return 0; + } + + vnode_recycle(vp); + + return 0; +} + +errno_t +fuzzed_vnop_reclaim(struct vnop_reclaim_args *ap) +{ + vnode_t vp = ap->a_vp; + vfs_context_t ctx = ap->a_context; + + vp->v_mount = NULL; + vp->v_op = NULL; + + return 0; +} + +const struct vnodeopv_entry_desc fuzzed_vnodeop_entries[] = { + {.opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)fuzzed_open}, + {.opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)fuzzed_close}, + {.opve_op = &vnop_access_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)fuzzed_vnop_getattr}, + {.opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)fuzzed_read}, + {.opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_mmap_check_desc, .opve_impl = (VOPFUNC)fuzzed_mmap_check}, + {.opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)fuzzed_fsync}, + {.opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_readlink_desc, + .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_inactive_desc, + .opve_impl = (VOPFUNC)fuzzed_vnop_inactive}, + {.opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)fuzzed_vnop_reclaim}, + {.opve_op = &vnop_strategy_desc, + .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)fuzzed_pathconf}, + {.opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_monitor_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_mnomap_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)fuzzed_pagein}, + {.opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_copyfile_desc, + .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_blktooff_desc, + .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_offtoblk_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_blockmap_desc, + .opve_impl = (VOPFUNC)unsupported_function}, + {.opve_op = &vnop_getxattr_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_listxattr_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_setxattr_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = &vnop_removexattr_desc, .opve_impl = (VOPFUNC)nop_function}, + {.opve_op = (struct vnodeop_desc *)NULL, .opve_impl = (VOPFUNC)NULL}}; + +const struct vnodeopv_desc fuzzed_vnodeop_opv_desc = { + .opv_desc_vector_p = &fuzzed_vnodeop_p, + .opv_desc_ops = fuzzed_vnodeop_entries}; diff --git a/fuzz/xnu/common/BUILD.bazel b/fuzz/xnu/common/BUILD.bazel new file mode 100644 index 0000000..3d63258 --- /dev/null +++ b/fuzz/xnu/common/BUILD.bazel @@ -0,0 +1,7 @@ +cc_library( + name = "test_utils", + srcs = ["test_utils.c"], + hdrs = ["test_utils.h"], + deps = ["//fuzz/host/hypercall:headers"], + visibility = ["//fuzz/xnu:__subpackages__"], +) diff --git a/fuzz/xnu/common/test_utils.c b/fuzz/xnu/common/test_utils.c new file mode 100644 index 0000000..2b9b79d --- /dev/null +++ b/fuzz/xnu/common/test_utils.c @@ -0,0 +1,61 @@ +/* + * Copyright 2024 Google LLC + * + * 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 "fuzz/xnu/common/test_utils.h" + +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void xnu_assert_helper(TestResult* result, bool condition, const char* file, + int line, const char* message) { + result->num_assertions++; + if (!condition) { + if (result->num_failures < MAX_ASSERTION_FAILURES) { + int ret = snprintf(result->failure_messages[result->num_failures], + sizeof(result->failure_messages[result->num_failures]), + "%s:%d: %s", file, line, message); + + if (ret < 0) { + // Handle snprintf error + if (is_verbose) { + printf("Error formatting assertion message\n"); + } + } else if ((size_t)ret >= + sizeof(result->failure_messages[result->num_failures])) { + // Handle truncation + if (is_verbose) { + printf("Assertion message truncated: %s:%d: %s\n", file, line, + message); + } + } + + result->num_failures++; + } else if (result->num_failures == MAX_ASSERTION_FAILURES) { + // Log that we've hit the maximum number of recorded failures + if (is_verbose) { + printf( + "Maximum number of assertion failures reached. Further failures " + "will not be recorded.\n"); + } + result->num_failures++; + } + + if (is_verbose) { + printf("Assertion failed at %s:%d: %s\n", file, line, message); + } + } +} diff --git a/fuzz/xnu/common/test_utils.h b/fuzz/xnu/common/test_utils.h new file mode 100644 index 0000000..d5adc79 --- /dev/null +++ b/fuzz/xnu/common/test_utils.h @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ + +#ifndef FUZZ_XNU_COMMON_TEST_UTILS_H_ +#define FUZZ_XNU_COMMON_TEST_UTILS_H_ + +#include + +#define MAX_ASSERTION_FAILURES 10 + +typedef struct { + int num_assertions; + int num_failures; + char failure_messages[MAX_ASSERTION_FAILURES][256]; +} __attribute__((aligned(128))) TestResult; + +void xnu_assert_helper(TestResult* result, bool condition, const char* file, + int line, const char* message); + +#define XNU_ASSERT(result, condition, message) \ + xnu_assert_helper(result, condition, __FILE__, __LINE__, message) + +#endif // FUZZ_XNU_COMMON_TEST_UTILS_H_ \ No newline at end of file diff --git a/fuzz/xnu/libxnu.redef b/fuzz/xnu/libxnu.redef new file mode 100644 index 0000000..7902b9b --- /dev/null +++ b/fuzz/xnu/libxnu.redef @@ -0,0 +1,88 @@ +accept xnu_accept +access xnu_access +atoi xnu_atoi +backtrace xnu_backtrace +exit xnu_exit +fgetxattr xnu_fgetxattr +flistxattr xnu_flistxattr +fork xnu_fork +fstatfs xnu_fstatfs +fstatfs64 xnu_fstatfs64 +ftruncate xnu_ftruncate +getgroups xnu_getgroups +getitimer xnu_getitimer +getpeername xnu_getpeername +getpid xnu_getpid +getrlimit xnu_getrlimit +getrusage xnu_getrusage +getsockname xnu_getsockname +getsockopt xnu_getsockopt +gettimeofday xnu_gettimeofday +getxattr xnu_getxattr +initgroups xnu_initgroups +ioctl xnu_ioctl +listxattr xnu_listxattr +log xnu_log +lseek xnu_lseek +lstat xnu_lstat +lstat64 xnu_lstat64 +memchr xnu_memchr +mincore xnu_mincore +mkdir xnu_mkdir +mlock xnu_mlock +mlockall xnu_mlockall +mmap xnu_mmap +mprotect xnu_mprotect +msgrcv xnu_msgrcv +msgsnd xnu_msgsnd +munlock xnu_munlock +munlockall xnu_munlockall +munmap xnu_munmap +open xnu_open +pipe xnu_pipe +poll xnu_poll +posix_spawn xnu_posix_spawn +pread xnu_pread +ptrace xnu_ptrace +pwrite xnu_pwrite +qsort xnu_qsort +read xnu_read +readlink xnu_readlink +readlinkat xnu_readlinkat +readv xnu_readv +recvfrom xnu_recvfrom +recvmsg xnu_recvmsg +sem_open xnu_sem_open +sem_post xnu_sem_post +sem_trywait xnu_sem_trywait +sem_unlink xnu_sem_unlink +sem_wait xnu_sem_wait +sendmsg xnu_sendmsg +sendto xnu_sendto +setitimer xnu_setitimer +setrlimit xnu_setrlimit +shm_open xnu_shm_open +shm_unlink xnu_shm_unlink +shmctl xnu_shmctl +sigaction xnu_sigaction +sigaltstack xnu_sigaltstack +sigpending xnu_sigpending +sigprocmask xnu_sigprocmask +sleep xnu_sleep +snprintf xnu_snprintf +sprintf xnu_sprintf +stat xnu_stat +stat64 xnu_stat64 +statfs xnu_statfs +statfs64 xnu_statfs64 +vsnprintf xnu_vsnprintf +vsprintf xnu_vsprintf +wait4 xnu_wait4 +waitid xnu_waitid +write xnu_write +writev xnu_writev +zalloc xnu_zalloc +zfree xnu_zfree +_memcpy memcpy +_memset memset +_strncpy strncpy diff --git a/fuzz/xnu/osfmk/BUILD.bazel b/fuzz/xnu/osfmk/BUILD.bazel new file mode 100644 index 0000000..3d5d08f --- /dev/null +++ b/fuzz/xnu/osfmk/BUILD.bazel @@ -0,0 +1,120 @@ +load("//third_party/xnu:options.bzl", "OSFMK_DEFINES", "OSFMK_INCLUDES", "XNU_COMPILE_OPTIONS", "XNU_INCLUDES", "convert_includes_to_flags") + +cc_library( + name = "api_headers", + hdrs = [ + "api/backend.h", + "api/scheduler_state.h", + "api/thread.h", + "api/types.h", + ], + visibility = [ + "//:__pkg__", + "//fuzz:__subpackages__", + ], +) + +cc_library( + name = "api", + srcs = [ + "api/osfmk_backend.c", + "api/scheduler_state.c", + "api/thread.c", + ], + copts = OSFMK_DEFINES + convert_includes_to_flags(XNU_INCLUDES + OSFMK_INCLUDES) + XNU_COMPILE_OPTIONS + ["-Wno-nullability-completeness"], + visibility = ["//fuzz/xnu:__pkg__"], + deps = [ + ":api_headers", + "//third_party/xnu:osfmk_internal_headers", + ], + alwayslink = 1, +) + +# TODO(nedwill): this isn't osfmk specific, move to //fuzz/xnu/common +cc_library( + name = "fake_headers", + hdrs = [ + "fakes/atomic.h", + ], + visibility = [ + "//fuzz/host/hypercall:__pkg__", + "//fuzz/xnu:__subpackages__", + "//third_party/xnu:__pkg__", + ], + # includes = ["fakes"], + # strip_include_prefix = "fakes", +) + +cc_library( + name = "fakes", + srcs = [ + "fakes/atomic.c", + "fakes/clock.c", + "fakes/continuation.c", + "fakes/copyio.c", + "fakes/cpu_data.c", + "fakes/locks.c", + "fakes/log.c", + "fakes/mach_notify.c", + "fakes/machine.c", + "fakes/osfmk_stubs.c", + "fakes/platform_expert.c", + "fakes/pmap.c", + "fakes/scheduler.c", + "fakes/thread.c", + "fakes/timer.c", + "fakes/zalloc.c", + ], + copts = OSFMK_DEFINES + convert_includes_to_flags(XNU_INCLUDES + OSFMK_INCLUDES) + XNU_COMPILE_OPTIONS, + linkstatic = 1, + visibility = ["//fuzz/xnu:__pkg__"], + deps = ["//third_party/xnu:osfmk_internal"], + alwayslink = 1, +) + +# TODO(nedwill): consider moving this to //fuzz/common since it has no +# dependency on XNU code. +cc_library( + name = "sync_fakes", + srcs = [ + "fakes/sync/attr.cc", + "fakes/sync/group.cc", + "fakes/sync/locks.cc", + "fakes/sync/mutex.cc", + "fakes/sync/rw_lock.cc", + "fakes/sync/spin.cc", + "fakes/sync/tlock.cc", + ], + linkstatic = 1, + visibility = [ + "//:__pkg__", + "//fuzz:__subpackages__", + ], + deps = [ + "//fuzz/host/hypercall:headers", + ], + alwayslink = 1, +) + +cc_library( + name = "test_impl_header", + hdrs = ["test/osfmk_test_impl.h"], + includes = ["test"], + deps = ["//fuzz/xnu/common:test_utils"], + visibility = ["//fuzz/host:__pkg__"], +) + +cc_library( + name = "test_impl", + srcs = ["test/osfmk_test_impl.c"], + deps = [ + ":test_impl_header", + "//fuzz/xnu/common:test_utils", + "//third_party/xnu:osfmk_internal_headers", + ], + # The following copts and deps are necessary to compile the test implementation + # with the same flags and dependencies as the actual XNU OSFMK code. This ensures + # that the test environment closely matches the real XNU environment. + copts = OSFMK_DEFINES + convert_includes_to_flags(XNU_INCLUDES + OSFMK_INCLUDES) + XNU_COMPILE_OPTIONS, + visibility = ["//fuzz/host:__pkg__"], +) \ No newline at end of file diff --git a/fuzz/xnu/osfmk/api/backend.h b/fuzz/xnu/osfmk/api/backend.h new file mode 100644 index 0000000..7c7c409 --- /dev/null +++ b/fuzz/xnu/osfmk/api/backend.h @@ -0,0 +1,165 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef OSFMK_BACKEND_H_ +#define OSFMK_BACKEND_H_ + +#ifndef LIBXNU_BUILD +#ifdef __cplusplus +#include +#include +#else +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "types.h" +void thread_set_thread_name(thread_t th, const char* name); +void thread_deallocate(thread_t thread); +void ast_taken_user(); + +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +kern_return_t _kernelrpc_mach_vm_allocate_trap_wrapper(mach_port_name_t target, + mach_vm_offset_t addr, + mach_vm_size_t size, + int flags); +kern_return_t _kernelrpc_mach_vm_purgable_control_trap_wrapper( + mach_port_name_t target, mach_vm_offset_t address, vm_purgable_t control, + user_addr_t state); +kern_return_t _kernelrpc_mach_vm_deallocate_trap_wrapper( + mach_port_name_t target, mach_vm_address_t address, mach_vm_size_t size); +kern_return_t task_dyld_process_info_notify_get_trap_wrapper( + mach_vm_address_t names_addr, mach_vm_address_t names_count_addr); +kern_return_t _kernelrpc_mach_vm_protect_trap_wrapper(mach_port_name_t target, + mach_vm_address_t address, + mach_vm_size_t size, + boolean_t set_maximum, + vm_prot_t new_protection); +kern_return_t _kernelrpc_mach_vm_map_trap_wrapper( + mach_port_name_t target, user_addr_t addr, mach_vm_size_t size, + mach_vm_offset_t mask, int flags, vm_prot_t cur_protection); +kern_return_t _kernelrpc_mach_port_allocate_trap_wrapper( + mach_port_name_t target, mach_port_right_t right, user_addr_t name); +kern_return_t _kernelrpc_mach_port_deallocate_trap_wrapper( + mach_port_name_t target, mach_port_name_t name); +kern_return_t _kernelrpc_mach_port_mod_refs_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_right_t right, + mach_port_delta_t delta); +kern_return_t _kernelrpc_mach_port_move_member_trap_wrapper( + mach_port_name_t target, mach_port_name_t member, mach_port_name_t after); +kern_return_t _kernelrpc_mach_port_insert_right_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_name_t poly, + mach_msg_type_name_t polyPoly); +kern_return_t _kernelrpc_mach_port_insert_member_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_name_t pset); +kern_return_t _kernelrpc_mach_port_extract_member_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_name_t pset); +kern_return_t _kernelrpc_mach_port_construct_trap_wrapper( + mach_port_name_t target, user_addr_t options, uint64_t context, + user_addr_t name); +kern_return_t _kernelrpc_mach_port_destruct_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_delta_t srdelta, + uint64_t guard); +kern_return_t _kernelrpc_mach_port_request_notification_trap_wrapper( + mach_port_name_t task, mach_port_name_t name, mach_msg_id_t msgid, + mach_port_mscount_t sync, mach_port_name_t notify, + mach_msg_type_name_t notifyPoly, mach_port_name_t* previous); +kern_return_t mach_reply_port_wrapper(); +kern_return_t thread_self_trap_wrapper(); +kern_return_t task_self_trap_wrapper(); +kern_return_t host_self_trap_wrapper(); +kern_return_t mach_msg_trap_wrapper(user_addr_t msg, mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_msg_priority_t priority, + user_addr_t rcv_msg); +mach_msg_return_t mach_msg2_trap_wrapper(mach_vm_address_t data, + mach_msg_option64_t options, + uint64_t msgh_bits_and_send_size, + uint64_t msgh_remote_and_local_port, + uint64_t msgh_voucher_and_id, + uint64_t desc_count_and_rcv_name, + uint64_t rcv_size_and_priority, + uint64_t timeout); +kern_return_t mach_msg_overwrite_trap_wrapper( + user_addr_t msg, mach_msg_option_t option, mach_msg_size_t send_size, + mach_msg_size_t rcv_size, mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, mach_msg_priority_t priority, + user_addr_t rcv_msg); +kern_return_t host_create_mach_voucher_trap_wrapper( + mach_port_name_t host, mach_voucher_attr_raw_recipe_array_t recipes, + int recipes_size, user_addr_t voucher); +kern_return_t mach_voucher_extract_attr_recipe_trap_wrapper( + mach_port_name_t voucher_name, mach_voucher_attr_key_t key, + mach_voucher_attr_raw_recipe_t recipe, user_addr_t recipe_size); +kern_return_t thread_get_special_reply_port_wrapper(); +kern_return_t mk_timer_create_trap_wrapper(); +kern_return_t mk_timer_destroy_trap_wrapper(mach_port_name_t name); +kern_return_t mk_timer_arm_trap_wrapper(mach_port_name_t name, + uint64_t expire_time); +kern_return_t mk_timer_cancel_trap_wrapper(mach_port_name_t name, + uint64_t result_time); +kern_return_t mk_timer_arm_leeway_trap_wrapper(mach_port_name_t name, + uint64_t mk_timer_flags, + uint64_t expire_time, + uint64_t mk_leeway); + +void DoRandomMachTrap(unsigned int number, void* data, size_t size); + +thread_t CloneTaskFromInit(); +task_t GetTaskFromThread(thread_t thread); + +void DoVmFault(uint64_t address, int type); + +#ifdef __cplusplus +} +#endif + +#endif /* OFSMK_BACKEND_H_ */ diff --git a/fuzz/xnu/osfmk/api/osfmk_backend.c b/fuzz/xnu/osfmk/api/osfmk_backend.c new file mode 100644 index 0000000..4f87b7b --- /dev/null +++ b/fuzz/xnu/osfmk/api/osfmk_backend.c @@ -0,0 +1,720 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/xnu/osfmk/api/backend.h" +#include "fuzz/xnu/osfmk/api/thread.h" +#include "scheduler_state.h" +#include "third_party/xnu/osfmk/kern/syscall_sw.h" +#include "third_party/xnu/osfmk/vm/vm_fault.h" + +struct proc; + +int setjmp(void *env); + +// Simplified i386_init +void osfmk_bootstrap() { + processor_bootstrap(); + thread_t bootstrap_thread = thread_bootstrap(); + machine_set_current_thread(bootstrap_thread); +} + +// struct _kernelrpc_mach_vm_allocate_trap_args { +// PAD_ARG_(mach_port_name_t, target); /* 1 word */ +// PAD_ARG_(user_addr_t, addr); /* 1 word */ +// PAD_ARG_(mach_vm_size_t, size); /* 2 words */ +// PAD_ARG_(int, flags); /* 1 word */ +// }; /* Total: 5 */ + +kern_return_t _kernelrpc_mach_vm_allocate_trap_wrapper(mach_port_name_t target, + mach_vm_offset_t addr, + mach_vm_size_t size, + int flags) { + struct _kernelrpc_mach_vm_allocate_trap_args args = { + .target = target, + .addr = addr, + .size = size, + .flags = flags, + }; + + int rv = _kernelrpc_mach_vm_allocate_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_vm_purgable_control_trap_wrapper( + mach_port_name_t target, mach_vm_offset_t address, vm_purgable_t control, + user_addr_t state) { + struct _kernelrpc_mach_vm_purgable_control_trap_args args = { + .target = target, + .address = address, + .control = control, + .state = state, + }; + + int rv = _kernelrpc_mach_vm_purgable_control_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_vm_deallocate_trap_wrapper( + mach_port_name_t target, mach_vm_address_t address, mach_vm_size_t size) { + struct _kernelrpc_mach_vm_deallocate_args args = { + .target = target, + .address = address, + .size = size, + }; + + int rv = _kernelrpc_mach_vm_deallocate_trap(&args); + return rv; +} + +// struct task_dyld_process_info_notify_get_trap_args { +// PAD_ARG_(mach_vm_address_t, names_addr); /* 2 words */ +// PAD_ARG_(mach_vm_address_t, names_count_addr); /* 2 words */ +// }; /* Total: 4 */ + +OS_OVERLOADABLE +extern void counter_dec_preemption_disabled(counter_t *counter) { + (*counter)--; +} + +kern_return_t task_dyld_process_info_notify_get_trap_wrapper( + mach_vm_address_t names_addr, mach_vm_address_t names_count_addr) { + struct task_dyld_process_info_notify_get_trap_args args = { + .names_addr = names_addr, + .names_count_addr = names_count_addr, + }; + + int rv = task_dyld_process_info_notify_get_trap(&args); + return rv; +} + +// struct _kernelrpc_mach_vm_protect_args { +// mach_port_name_t target, +// mach_vm_address_t address, +// mach_vm_size_t size, +// boolean_t set_maximum, +// vm_prot_t new_protection, +// }; /* Total: 7 */ + +kern_return_t _kernelrpc_mach_vm_protect_trap_wrapper( + mach_port_name_t target, mach_vm_address_t address, mach_vm_size_t size, + boolean_t set_maximum, vm_prot_t new_protection) { + struct _kernelrpc_mach_vm_protect_args args = { + .target = target, + .address = address, + .size = size, + .set_maximum = set_maximum, + .new_protection = new_protection, + }; + + int rv = _kernelrpc_mach_vm_protect_trap(&args); + return rv; +} + +// struct _kernelrpc_mach_vm_map_trap_args { +// mach_port_name_t target, +// user_addr_t addr, +// mach_vm_size_t size, +// mach_vm_offset_t mask, +// int flags, +// vm_prot_t cur_protection, +// }; + +kern_return_t _kernelrpc_mach_vm_map_trap_wrapper( + mach_port_name_t target, user_addr_t addr, mach_vm_size_t size, + mach_vm_offset_t mask, int flags, vm_prot_t cur_protection) { + struct _kernelrpc_mach_vm_map_trap_args args = { + .target = target, + .addr = addr, + .size = size, + .mask = mask, + .flags = flags, + .cur_protection = cur_protection, + }; + + int rv = _kernelrpc_mach_vm_map_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_allocate_trap_wrapper( + mach_port_name_t target, mach_port_right_t right, user_addr_t name) { + struct _kernelrpc_mach_port_allocate_args args = { + .target = target, .right = right, .name = name}; + + int rv = _kernelrpc_mach_port_allocate_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_deallocate_trap_wrapper( + mach_port_name_t target, mach_port_name_t name) { + struct _kernelrpc_mach_port_deallocate_args args = {.target = target, + .name = name}; + + int rv = _kernelrpc_mach_port_deallocate_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_mod_refs_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_right_t right, + mach_port_delta_t delta) { + struct _kernelrpc_mach_port_mod_refs_args args = { + .target = target, .name = name, .right = right, .delta = delta}; + + int rv = _kernelrpc_mach_port_mod_refs_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_move_member_trap_wrapper( + mach_port_name_t target, mach_port_name_t member, mach_port_name_t after) { + struct _kernelrpc_mach_port_move_member_args args = { + .target = target, .member = member, .after = after}; + + int rv = _kernelrpc_mach_port_move_member_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_insert_right_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_name_t poly, + mach_msg_type_name_t polyPoly) { + struct _kernelrpc_mach_port_insert_right_args args = { + .target = target, .name = name, .poly = poly, .polyPoly = polyPoly}; + + int rv = _kernelrpc_mach_port_insert_right_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_insert_member_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_name_t pset) { + struct _kernelrpc_mach_port_insert_member_args args = { + .target = target, .name = name, .pset = pset}; + + int rv = _kernelrpc_mach_port_insert_member_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_extract_member_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_name_t pset) { + struct _kernelrpc_mach_port_extract_member_args args = { + .target = target, .name = name, .pset = pset}; + + int rv = _kernelrpc_mach_port_extract_member_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_construct_trap_wrapper( + mach_port_name_t target, user_addr_t options, uint64_t context, + user_addr_t name) { + struct _kernelrpc_mach_port_construct_args args = { + .target = target, .options = options, .context = context, .name = name}; + + int rv = _kernelrpc_mach_port_construct_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_destruct_trap_wrapper( + mach_port_name_t target, mach_port_name_t name, mach_port_delta_t srdelta, + uint64_t guard) { + struct _kernelrpc_mach_port_destruct_args args = { + .target = target, .name = name, .srdelta = srdelta, .guard = guard}; + + int rv = _kernelrpc_mach_port_destruct_trap(&args); + return rv; +} + +kern_return_t mach_reply_port_wrapper() { + struct mach_reply_port_args args = {}; + + int rv = mach_reply_port(&args); + return rv; +} + +kern_return_t thread_self_trap_wrapper() { + struct thread_self_trap_args args = {}; + + int rv = thread_self_trap(&args); + return rv; +} + +kern_return_t task_self_trap_wrapper() { + struct task_self_trap_args args = {}; + + int rv = task_self_trap(&args); + return rv; +} + +kern_return_t host_self_trap_wrapper() { + struct host_self_trap_args args = {}; + + int rv = host_self_trap(&args); + return rv; +} + +kern_return_t mach_msg_trap_wrapper(user_addr_t msg, mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_msg_priority_t priority, + user_addr_t rcv_msg) { + struct mach_msg_overwrite_trap_args args = {.msg = msg, + .option = option, + .send_size = send_size, + .rcv_size = rcv_size, + .rcv_name = rcv_name, + .timeout = timeout, + .priority = priority, + .rcv_msg = rcv_msg}; + + int rv = mach_msg_trap(&args); + return rv; +} + +mach_msg_return_t mach_msg2_trap_wrapper( + mach_vm_address_t data, mach_msg_option64_t options, + uint64_t msgh_bits_and_send_size, uint64_t msgh_remote_and_local_port, + uint64_t msgh_voucher_and_id, uint64_t desc_count_and_rcv_name, + uint64_t rcv_size_and_priority, uint64_t timeout) { + struct mach_msg2_trap_args args = { + .data = data, + .options = options, + .msgh_bits_and_send_size = msgh_bits_and_send_size, + .msgh_remote_and_local_port = msgh_remote_and_local_port, + .msgh_voucher_and_id = msgh_voucher_and_id, + .desc_count_and_rcv_name = desc_count_and_rcv_name, + .rcv_size_and_priority = rcv_size_and_priority, + .timeout = timeout}; + + mach_msg_return_t rv = mach_msg2_trap(&args); + return rv; +} + +kern_return_t mach_msg_overwrite_trap_wrapper( + user_addr_t msg, mach_msg_option_t option, mach_msg_size_t send_size, + mach_msg_size_t rcv_size, mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, mach_msg_priority_t priority, + user_addr_t rcv_msg) { + struct mach_msg_overwrite_trap_args args = {.msg = msg, + .option = option, + .send_size = send_size, + .rcv_size = rcv_size, + .rcv_name = rcv_name, + .timeout = timeout, + .priority = priority, + .rcv_msg = rcv_msg}; + + int rv = mach_msg_overwrite_trap(&args); + return rv; +} + +kern_return_t host_create_mach_voucher_trap_wrapper( + mach_port_name_t host, mach_voucher_attr_raw_recipe_array_t recipes, + int recipes_size, user_addr_t voucher) { + struct host_create_mach_voucher_args args = { + .host = host, + .recipes = recipes, + .recipes_size = recipes_size, + .voucher = voucher, + }; + + int rv = host_create_mach_voucher_trap(&args); + return rv; +} + +kern_return_t mach_voucher_extract_attr_recipe_trap_wrapper( + mach_port_name_t voucher_name, mach_voucher_attr_key_t key, + mach_voucher_attr_raw_recipe_t recipe, user_addr_t recipe_size) { + struct mach_voucher_extract_attr_recipe_args args = { + .voucher_name = voucher_name, + .key = key, + .recipe = recipe, + .recipe_size = recipe_size, + }; + + int rv = mach_voucher_extract_attr_recipe_trap(&args); + return rv; +} + +kern_return_t _kernelrpc_mach_port_request_notification_trap_wrapper( + mach_port_name_t task, mach_port_name_t name, mach_msg_id_t msgid, + mach_port_mscount_t sync, mach_port_name_t notify, + mach_msg_type_name_t notifyPoly, mach_port_name_t *previous) { + struct _kernelrpc_mach_port_request_notification_args args = { + .target = task, + .name = name, + .msgid = msgid, + .sync = sync, + .notify = notify, + .notifyPoly = notifyPoly, + .previous = (user_addr_t)previous}; + + int rv = _kernelrpc_mach_port_request_notification_trap(&args); + return rv; +} + +kern_return_t thread_create_running(task_t task, int flavor, + thread_state_t new_state, + mach_msg_type_number_t new_state_count, + thread_t *new_thread); + +void *get_init_task(); + +// kern_return_t +// thread_create_running( +// task_t task, +// int flavor, +// thread_state_t new_state, +// mach_msg_type_number_t new_state_count, +// thread_t *new_thread) +thread_t CreateKernelThread() { + thread_t thread = NULL; + // kern_return_t ret = + kernel_thread_create(/* continuation */ NULL, + /* parameter */ NULL, /* priority */ 0, &thread); + thread->t_task = kernel_task; + return thread; +} + +// __attribute__((visibility("default"))) +// thread_t CreateInitThread() { +// thread_t thread = NULL; +// task_t init_task = get_init_task(); +// if (thread_create_running(init_task, 0, 0, 0, &thread)) { +// return NULL; +// } +// return thread; +// } + +void XNUThreadGetThreadName(thread_t th, char *name) { + thread_get_thread_name(th, name); +} + +thread_t CreateWaitingThread(task_t task) { + thread_t thread = NULL; + + if (main_thread_create_waiting(task, (thread_continue_t)task_wait_to_return, + task_get_return_wait_event(task), &thread)) { + return NULL; + } + // if (thread_create_running(task, 0, 0, 0, &thread)) { + // return NULL; + // } + return thread; +} + +task_t GetTaskFromThread(thread_t thread) { + return thread->t_task; +} + +typedef struct proc *proc_t; + +void proc_signalend(struct proc *, int locked); +void proc_transend(proc_t p, int locked); + +thread_t cloneproc(task_t, coalition_t *, proc_t, int, int); +thread_t CloneTask(task_t task) { + // TODO(nedwill): consider using posix_spawn syscall instead + thread_t thread = + cloneproc(TASK_NULL, NULL, get_bsdtask_info(task), FALSE, FALSE); + proc_signalend(get_bsdtask_info(thread->t_task), 0); + proc_transend(get_bsdtask_info(thread->t_task), 0); + // TODO(nedwill): consider supporting this + // act_set_astbsd(thread); + return thread; +} + +void ClearTaskWait(task_t task) { + task_clear_return_wait(task, TCRW_CLEAR_ALL_WAIT); + // We can now safely drop the extra reference we got when cloning + task_deallocate(task); +} + +thread_t CloneTaskFromInit() { + return CloneTask(get_init_task()); +} + +proc_t GetProcFromTask(task_t task) { + return get_bsdtask_info(task); +} + +kern_return_t thread_get_special_reply_port_wrapper() { + struct thread_get_special_reply_port_args args = {}; + + int rv = thread_get_special_reply_port(&args); + return rv; +} + +kern_return_t mk_timer_create_trap_wrapper() { + struct mk_timer_create_trap_args args = {}; + + int rv = mk_timer_create_trap(&args); + return rv; +} + +kern_return_t mk_timer_destroy_trap_wrapper(mach_port_name_t name) { + struct mk_timer_destroy_trap_args args = {.name = name}; + + int rv = mk_timer_destroy_trap(&args); + return rv; +} + +kern_return_t mk_timer_arm_trap_wrapper(mach_port_name_t name, + uint64_t expire_time) { + struct mk_timer_arm_trap_args args = {.name = name, + .expire_time = expire_time}; + + int rv = mk_timer_arm_trap(&args); + return rv; +} + +kern_return_t mk_timer_cancel_trap_wrapper(mach_port_name_t name, + uint64_t result_time) { + struct mk_timer_cancel_trap_args args = {.name = name, + .result_time = result_time}; + + int rv = mk_timer_cancel_trap(&args); + return rv; +} + +kern_return_t mk_timer_arm_leeway_trap_wrapper(mach_port_name_t name, + uint64_t mk_timer_flags, + uint64_t expire_time, + uint64_t mk_leeway) { + struct mk_timer_arm_leeway_trap_args args = {.name = name, + .mk_timer_flags = mk_timer_flags, + .expire_time = expire_time, + .mk_leeway = mk_leeway}; + + int rv = mk_timer_arm_leeway_trap(&args); + return rv; +} + +void *calloc(size_t a, size_t b); +void free(void *ptr); + +void DoRandomMachTrap(unsigned int number, void *data, size_t size) { + number %= mach_trap_count; + const mach_trap_t *entry = &mach_trap_table[number]; + size_t argument_size = entry->mach_trap_arg_count * 8; + void *arguments = calloc(1, argument_size); + if (!arguments) { + return; + } + if (size > argument_size) { + size = argument_size; + } + int retval; + GetHypercallInterface()->BlockCopyin(); + entry->mach_trap_function(arguments); + GetHypercallInterface()->UnblockCopyin(); + free(arguments); +} + +// TODO(nedwill): move the following section into concurrence test +lck_mtx_t test_mtx; +lck_rw_t test_rw; + +bool sync_initialized = false; + +enum LOCK_TEST { + YIELD = 0, + MUTEX = 1, + READ_WRITE_SHARED = 2, + READ_WRITE_EXCLUSIVE = 3, + MUTEX2 = 4, + READ_WRITE_SHARED2 = 5, + READ_WRITE_EXCLUSIVE2 = 6, +}; + +size_t current_index = 0; +bool failed = false; +// If 0, entire array is checked. +size_t num_expected = 10; +int expected_order[] = {4, 0, 2, 2, 1, 4, 3, 4, 3, 0, 4, 0, 2, 2, 1, + 3, 0, 3, 2, 0, 1, 3, 1, 2, 0, 1, 4, 2, 3}; + +// These functions are called by the concurrence_test_fuzzer +void HostTestInit() { + if (!sync_initialized) { + lck_mtx_init(&test_mtx, NULL, NULL); + lck_rw_init(&test_rw, NULL, NULL); + } + current_index = 0; + failed = false; +} + +bool check_state(int id) { + // Check if another thread has already failed + GetHypercallInterface()->ThreadPrintf( + "checking id %d for current index %d, failed %d\n", id, current_index, + failed); + if (failed) { + return false; + } + if (current_index >= sizeof(expected_order) / sizeof(int)) { + GetHypercallInterface()->ThreadPrintf("check_state: succeeded\n"); + return true; + } + printf("current_index %d\n", current_index); + int expected_id = expected_order[current_index++]; + // Note we just incremented so this is the last check + GetHypercallInterface()->ThreadPrintf("id %d, expected_id %d\n", id, + expected_id); + if (id != expected_id) { + if (current_index >= num_expected) { + GetHypercallInterface()->ThreadPrintf( + "almost succeeded at index %d with id %d but required %d\n", + current_index - 1, id, expected_id); + CheckSchedulerState(); + if (GetHypercallInterface()->RandomDataRemaining()) { + // GetHypercallInterface()->Abort(); + } + // GetHypercallInterface()->Abort(); + } + GetHypercallInterface()->ThreadPrintf("check_state: failed 2\n"); + failed = true; + return false; + } + if (!num_expected) { + num_expected = sizeof(expected_order) / sizeof(int); + } + if (current_index >= num_expected) { + // Found the bug interleaving! + GetHypercallInterface()->Abort(); + } + + return true; +} + +void *HostTestThread(void *tid) { + int id = (int)tid; + while (true) { + bool passed = false; + switch (id) { + case YIELD: { + size_t before = GetHypercallInterface()->BytesNeeded(); + thread_yield_internal(1); + size_t after = GetHypercallInterface()->BytesNeeded(); + GetHypercallInterface()->ThreadPrintf("yield cost %d bytes\n", + after - before); + passed = check_state(id); + break; + } + case MUTEX: + case MUTEX2: { + size_t before = GetHypercallInterface()->BytesNeeded(); + if (id == MUTEX2) { + GetHypercallInterface()->ThreadPrintf("mutex2 before lock\n"); + } + lck_mtx_lock(&test_mtx); + if (id == MUTEX2) { + GetHypercallInterface()->ThreadPrintf("mutex2 after lock\n"); + } + size_t after = GetHypercallInterface()->BytesNeeded(); + GetHypercallInterface()->ThreadPrintf("mutex lock cost %d bytes\n", + after - before); + if (id == MUTEX2) { + GetHypercallInterface()->ThreadPrintf("mutex2 before check_state\n"); + } + passed = check_state(id); + if (id == MUTEX2) { + GetHypercallInterface()->ThreadPrintf("mutex2 after check_state\n"); + } + before = GetHypercallInterface()->BytesNeeded(); + if (id == MUTEX2) { + GetHypercallInterface()->ThreadPrintf("mutex2 before unlock\n"); + } + lck_mtx_unlock(&test_mtx); + if (id == MUTEX2) { + GetHypercallInterface()->ThreadPrintf("mutex2 after unlock\n"); + } + after = GetHypercallInterface()->BytesNeeded(); + // GetHypercallInterface()->ThreadPrintf("mutex unlock cost %d bytes\n", after-before); + break; + } + case READ_WRITE_SHARED: + case READ_WRITE_SHARED2: { + size_t before = GetHypercallInterface()->BytesNeeded(); + lck_rw_lock_shared(&test_rw); + size_t after = GetHypercallInterface()->BytesNeeded(); + GetHypercallInterface()->ThreadPrintf("rw shared lock cost %d bytes\n", + after - before); + passed = check_state(id); + before = GetHypercallInterface()->BytesNeeded(); + lck_rw_unlock_shared(&test_rw); + after = GetHypercallInterface()->BytesNeeded(); + // GetHypercallInterface()->ThreadPrintf("rw shared unlock cost %d bytes\n", + // after-before); + break; + } + case READ_WRITE_EXCLUSIVE: + case READ_WRITE_EXCLUSIVE2: { + size_t before = GetHypercallInterface()->BytesNeeded(); + lck_rw_lock_exclusive(&test_rw); + size_t after = GetHypercallInterface()->BytesNeeded(); + GetHypercallInterface()->ThreadPrintf( + "rw exclusive lock cost %d bytes\n", after - before); + passed = check_state(id); + before = GetHypercallInterface()->BytesNeeded(); + lck_rw_unlock_exclusive(&test_rw); + after = GetHypercallInterface()->BytesNeeded(); + // GetHypercallInterface()->ThreadPrintf("rw exclusive unlock cost %d bytes\n", + // after-before); + break; + } + default: { + GetHypercallInterface()->Abort(); + break; + } + } + if (!passed) { + break; + } + } + + return NULL; +} + +void DoVmFault(uint64_t address, vm_prot_t type) { + vm_fault(current_thread()->map, address, type, + /* change_wiring */ FALSE, VM_KERN_MEMORY_NONE, THREAD_ABORTSAFE, + /* caller_pmap */ NULL, /* caller_pmap_addr */ 0); +} diff --git a/fuzz/xnu/osfmk/api/scheduler_state.c b/fuzz/xnu/osfmk/api/scheduler_state.c new file mode 100644 index 0000000..3766e8c --- /dev/null +++ b/fuzz/xnu/osfmk/api/scheduler_state.c @@ -0,0 +1,176 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// Expose internal scheduler state via a C++ object for assertion checking +#include "fuzz/xnu/osfmk/api/scheduler_state.h" + +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void print_task(task_t task) { + printf("task %p\n", task); +} + +void print_thread(thread_t thread) { + if (!is_verbose) { + return; + } + if (!thread) { + return; + } + + // printf("wait %d/runq %p/waitq %p/wake %d/wait result %d\n", + // thread->wait_event, thread->runq, thread->waitq, thread->wake_active, + // thread->wait_result); + printf("thread %p: ", thread); + bool printed = false; + if (GetHypercallInterface()->ThreadIsWaiting(thread)) { + printf("TH_WAIT"); + printed = true; + } + if (GetHypercallInterface()->ThreadIsSuspended(thread)) { + if (printed) { + printf(", "); + } + printf("TH_SUSP"); + printed = true; + } + if (GetHypercallInterface()->ThreadIsRunnable(thread)) { + if (printed) { + printf(", "); + } + printf("TH_RUN"); + printed = true; + } + if (GetHypercallInterface()->ThreadIsUninterruptible(thread)) { + if (printed) { + printf(", "); + } + printf("TH_UNINT"); + printed = true; + } + if (GetHypercallInterface()->ThreadIsTerminating(thread)) { + if (printed) { + printf(", "); + } + printf("TH_TERMINATE"); + printed = true; + } + if (GetHypercallInterface()->ThreadIsEnqueuedForTermination(thread)) { + if (printed) { + printf(", "); + } + printf("TH_TERMINATE2"); + printed = true; + } + if (GetHypercallInterface()->ThreadWantsWaitReport(thread)) { + if (printed) { + printf(", "); + } + printf("TH_WAIT_REPORT"); + printed = true; + } + if (GetHypercallInterface()->ThreadIsIdle(thread)) { + if (printed) { + printf(", "); + } + printf("TH_IDLE"); + printed = true; + } + // printf("continuation %p\n", thread->continuation); + GetHypercallInterface()->PrintThreadState(thread); + // TODO(nedwill): investigate why this crashes sometimes + // HostPrintBacktrace(thread); +} + +void CheckSchedulerState() { + if (!is_verbose) { + return; + } + // See kernelcore.py + task_t task = NULL; + printf("total tasks expected %d\n", tasks_count); + queue_iterate(&tasks, task, task_t, tasks) { + print_task(task); + } + + thread_t thread = NULL; + printf("total threads expected %d\n", threads_count); + queue_iterate(&threads, thread, thread_t, threads) { + print_thread(thread); + } +} + +task_t GetTask(task_t task) { + if (!task) { + if (queue_empty(&tasks)) { + return NULL; + } + return (task_t)queue_first(&tasks); + } + task = (task_t)queue_next(&task->tasks); + if (task == (task_t)&tasks) { + return NULL; + } + return task; +} + +thread_t GetThread(thread_t thread) { + if (!thread) { + if (queue_empty(&threads)) { + return NULL; + } + return (thread_t)queue_first(&threads); + } + thread = (thread_t)queue_next(&thread->threads); + if (thread == (thread_t)&threads) { + return NULL; + } + return thread; +} + +void GetThreadState(thread_t thread, thread_debug_state_t state) { + state->waiting = GetHypercallInterface()->ThreadIsWaiting(thread); + state->suspended = GetHypercallInterface()->ThreadIsSuspended(thread); + state->runnable = GetHypercallInterface()->ThreadIsRunnable(thread); + state->uninterruptible = GetHypercallInterface()->ThreadIsUninterruptible(thread); + state->terminate = GetHypercallInterface()->ThreadIsTerminating(thread); + state->terminate2 = GetHypercallInterface()->ThreadIsEnqueuedForTermination(thread); + state->wait_report = GetHypercallInterface()->ThreadWantsWaitReport(thread); + state->idle = GetHypercallInterface()->ThreadIsIdle(thread); + state->wait_event = thread->wait_event; + state->runq = thread->__runq.runq; + state->waitq = &thread->waitq; + state->wake_active = thread->wake_active; + state->wait_result = GetHypercallInterface()->ThreadGetWaitResult(thread); +} diff --git a/fuzz/xnu/osfmk/api/scheduler_state.h b/fuzz/xnu/osfmk/api/scheduler_state.h new file mode 100644 index 0000000..381b7c7 --- /dev/null +++ b/fuzz/xnu/osfmk/api/scheduler_state.h @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef SCHEDULER_STATE_H_ +#define SCHEDULER_STATE_H_ + +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LIBXNU_BUILD +#include +#include +#else +#include "types.h" +#endif + +struct thread_debug_state { + bool waiting; + bool suspended; + bool runnable; + bool uninterruptible; + bool terminate; + bool terminate2; + bool wait_report; + bool idle; + uint64_t wait_event; + void *runq; + void *waitq; + bool wake_active; + int wait_result; +}; + +typedef struct thread_debug_state *thread_debug_state_t; + +void GetThreadState(thread_t thread, thread_debug_state_t state); + +void CheckSchedulerState(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fuzz/xnu/osfmk/api/thread.c b/fuzz/xnu/osfmk/api/thread.c new file mode 100644 index 0000000..308de58 --- /dev/null +++ b/fuzz/xnu/osfmk/api/thread.c @@ -0,0 +1,201 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/xnu/osfmk/api/thread.h" + +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void XNUThreadEnqueueTermination(thread_t thread) { + thread_terminate_enqueue(thread); +} + +void XNUThreadSchedCallNotifyBlocked(thread_t thread) { + (*thread->sched_call)(SCHED_CALL_BLOCK, thread); +} + +void XNUThreadSchedCallNotifyUnblocked(thread_t thread) { + (*thread->sched_call)(SCHED_CALL_UNBLOCK, thread); +} + +void XNUThreadCancelWaitTimer(thread_t thread) { + if (thread->wait_timer_armed) { + if (timer_call_cancel(thread->wait_timer)) { + thread->wait_timer_active--; + } + thread->wait_timer_armed = false; + } +} + +void XNUThreadSignalWake(thread_t thread) { + if (thread->wake_active) { + thread->wake_active = false; + thread_wakeup(&thread->wake_active); + } +} + +bool XNUWaitInterruptIsAbortSafe(wait_interrupt_t wait_interrupt) { + return (wait_interrupt & TH_OPT_INTMASK) == THREAD_ABORTSAFE; +} + +static bool XNUWaitInterruptIsUninterruptible(wait_interrupt_t wait_interrupt) { + return (wait_interrupt & TH_OPT_INTMASK) == THREAD_UNINT; +} + +bool XNUThreadSchedulerStateIsAbortSafely(thread_t thread) { + return thread->sched_flags & TH_SFLAG_ABORTSAFELY; +} + +wait_result_t XNUThreadMarkWaitLocked(thread_t thread, + wait_interrupt_t interruptible_orig) { + boolean_t at_safe_point = 0; + wait_interrupt_t interruptible = interruptible_orig; + +#ifndef NDEBUG + if (GetHypercallInterface()->ThreadIsWaiting(thread) || + GetHypercallInterface()->ThreadIsIdle(thread) || + GetHypercallInterface()->ThreadIsUninterruptible(thread) || + GetHypercallInterface()->ThreadIsEnqueuedForTermination(thread) || + GetHypercallInterface()->ThreadWantsWaitReport(thread)) { + GetHypercallInterface()->Abort(); + } +#endif + + interruptible &= TH_OPT_INTMASK; + + at_safe_point = XNUWaitInterruptIsAbortSafe(interruptible_orig); + + if (XNUWaitInterruptIsUninterruptible(interruptible_orig) || + !(thread->sched_flags & TH_SFLAG_ABORT) || + (!at_safe_point && XNUThreadSchedulerStateIsAbortSafely(thread))) { + GetHypercallInterface()->ThreadSetWaiting(thread); + if (!interruptible) { + GetHypercallInterface()->ThreadSetUninterruptible(thread); + } + if (thread->sched_call) { + wait_interrupt_t mask = THREAD_WAIT_NOREPORT_USER; + if (is_kerneltask(get_threadtask(thread))) { + mask = THREAD_WAIT_NOREPORT_KERNEL; + } + if ((interruptible_orig & mask) == 0) { + GetHypercallInterface()->ThreadSetWaitReport(thread); + } + } + thread->at_safe_point = at_safe_point; + GetHypercallInterface()->ThreadSetWaitResult(thread, THREAD_WAITING); + + return THREAD_WAITING; + } + if (thread->sched_flags & TH_SFLAG_ABORTSAFELY) { + thread->sched_flags &= ~TH_SFLAG_ABORTED_MASK; + } + + GetHypercallInterface()->ThreadSetWaitResult(thread, THREAD_INTERRUPTED); + return THREAD_INTERRUPTED; +} + +void cause_ast_check(processor_t processor) { + (void)processor; + GetHypercallInterface()->Abort(); +} + +bool XNUThreadStop(thread_t thread, boolean_t until_not_runnable) { + wait_result_t wresult = 0; + + while (GetHypercallInterface()->ThreadIsSuspended(thread)) { + thread->wake_active = true; + + wresult = XNUAssertWait(&thread->wake_active, THREAD_ABORTSAFE); + if (wresult == THREAD_WAITING) { + wresult = GetHypercallInterface()->ThreadBlock(THREAD_CONTINUE_NULL); + } + if (wresult != THREAD_AWAKENED) { + return false; + } + } + + GetHypercallInterface()->ThreadSetSuspended(thread); + + while (until_not_runnable && + GetHypercallInterface()->ThreadIsRunnable(thread)) { + thread->wake_active = true; + + wresult = XNUAssertWait(&thread->wake_active, THREAD_ABORTSAFE); + + if (wresult == THREAD_WAITING) { + wresult = GetHypercallInterface()->ThreadBlock(THREAD_CONTINUE_NULL); + } + + if (wresult != THREAD_AWAKENED) { + GetHypercallInterface()->ThreadUnstop(thread); + return false; + } + } + + /* + * We return with the thread unlocked. To prevent it from + * transitioning to a runnable state (or from TH_RUN to + * being on the CPU), the caller must ensure the thread + * is stopped via an external means (such as an AST) + */ + + return true; +} + +wait_result_t XNUAssertWait(event_t event, wait_interrupt_t interruptible) { + return assert_wait(event, interruptible); +} + +void XNUThreadWait(thread_t thread, boolean_t until_not_runnable) { + while (until_not_runnable && + GetHypercallInterface()->ThreadIsRunnable(thread)) { + thread->wake_active = true; + wait_result_t wresult = XNUAssertWait(&thread->wake_active, THREAD_UNINT); + + if (wresult == THREAD_WAITING) { + GetHypercallInterface()->ThreadBlockReason(THREAD_CONTINUE_NULL, NULL); + } + } +} + +void thread_terminate(thread_t thread); + +void XNUThreadTerminate(thread_t thread) { + thread_terminate(thread); +} + +// Use XNU prefix since we generate protobuf class with ThreadAbortSafely +void XNUThreadAbort(thread_t thread) { + thread_abort(thread); +} diff --git a/fuzz/xnu/osfmk/api/thread.h b/fuzz/xnu/osfmk/api/thread.h new file mode 100644 index 0000000..5f19ded --- /dev/null +++ b/fuzz/xnu/osfmk/api/thread.h @@ -0,0 +1,77 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_XNU_OSFMK_API_THREAD_H_ +#define FUZZ_XNU_OSFMK_API_THREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LIBXNU_BUILD +#include +#include +#include +#include +#include +#include +#else +#include "types.h" +#endif + +// Continuations +void XNUThreadSetContinuation(thread_t thread, thread_continue_t function, + void *parameter); + +// Wait timer +void XNUThreadCancelWaitTimer(thread_t thread); + +// Blocked +void XNUThreadSchedCallNotifyBlocked(thread_t thread); +void XNUThreadSchedCallNotifyUnblocked(thread_t thread); +void XNUThreadSignalWake(thread_t thread); + +wait_result_t XNUThreadMarkWaitLocked(thread_t thread, + wait_interrupt_t interruptible_orig); +bool XNUThreadStop(thread_t thread, boolean_t until_not_runnable); +void XNUThreadWait(thread_t thread, boolean_t until_not_runnable); + +wait_result_t XNUAssertWait(event_t event, wait_interrupt_t interruptible); + +void XNUThreadStart(); +void XNUThreadTerminate(thread_t thread); +void XNUThreadEnqueueTermination(thread_t thread); +void XNUThreadAbort(thread_t thread); +void XNUThreadGetThreadName(thread_t th, char *name); +void XNUGetThreadInfo(thread_t thread, char *data, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* FUZZ_XNU_OSFMK_API_THREAD_H_ */ diff --git a/fuzz/xnu/osfmk/api/types.h b/fuzz/xnu/osfmk/api/types.h new file mode 100644 index 0000000..16ef34f --- /dev/null +++ b/fuzz/xnu/osfmk/api/types.h @@ -0,0 +1,249 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef OSFMK_API_TYPES_H_ +#define OSFMK_API_TYPES_H_ + +#include +#include + +#define MACH_MSG_OPTION_NONE 0x00000000 + +#define MACH_SEND_MSG 0x00000001 +#define MACH_RCV_MSG 0x00000002 + +#define MACH_RCV_LARGE 0x00000004 /* report large message sizes */ +#define MACH_RCV_LARGE_IDENTITY \ + 0x00000008 /* identify source of large messages */ + +#define MACH_SEND_TIMEOUT 0x00000010 /* timeout value applies to send */ +#define MACH_SEND_OVERRIDE 0x00000020 /* priority override for send */ +#define MACH_SEND_INTERRUPT 0x00000040 /* don't restart interrupted sends */ +#define MACH_SEND_NOTIFY 0x00000080 /* arm send-possible notify */ +#define MACH_SEND_ALWAYS 0x00010000 /* ignore qlimits - kernel only */ +#define MACH_SEND_FILTER_NONFATAL \ + 0x00010000 /* rejection by message filter should return failure - user only */ +#define MACH_SEND_TRAILER 0x00020000 /* sender-provided trailer */ +#define MACH_SEND_NOIMPORTANCE 0x00040000 /* msg won't carry importance */ +#define MACH_SEND_NODENAP MACH_SEND_NOIMPORTANCE +#define MACH_SEND_IMPORTANCE \ + 0x00080000 /* msg carries importance - kernel only */ +#define MACH_SEND_SYNC_OVERRIDE \ + 0x00100000 /* msg should do sync IPC override (on legacy kernels) */ +#define MACH_SEND_PROPAGATE_QOS \ + 0x00200000 /* IPC should propagate the caller's QoS */ +#define MACH_SEND_SYNC_USE_THRPRI MACH_SEND_PROPAGATE_QOS /* obsolete name */ +#define MACH_SEND_KERNEL \ + 0x00400000 /* full send from kernel space - kernel only */ +#define MACH_SEND_SYNC_BOOTSTRAP_CHECKIN \ + 0x00800000 /* special reply port should boost thread doing sync bootstrap checkin */ + +#define MACH_RCV_TIMEOUT 0x00000100 /* timeout value applies to receive */ +#define MACH_RCV_NOTIFY 0x00000000 /* legacy name (value was: 0x00000200) */ +#define MACH_RCV_INTERRUPT 0x00000400 /* don't restart interrupted receive */ +#define MACH_RCV_VOUCHER 0x00000800 /* willing to receive voucher port */ +#define MACH_RCV_OVERWRITE 0x00000000 /* scatter receive (deprecated) */ +#define MACH_RCV_GUARDED_DESC \ + 0x00001000 /* Can receive new guarded descriptor */ +#define MACH_RCV_SYNC_WAIT 0x00004000 /* sync waiter waiting for rcv */ +#define MACH_RCV_SYNC_PEEK 0x00008000 /* sync waiter waiting to peek */ + +#define MACH_MSG_STRICT_REPLY \ + 0x00000200 /* Enforce specific properties about the reply port, and + * the context in which a thread replies to a message. + * This flag must be passed on both the SEND and RCV */ + +typedef uint32_t mach_port_name_t; +typedef uintptr_t mach_vm_offset_t; +typedef size_t mach_vm_size_t; +typedef int vm_purgable_t; +typedef uint64_t user_addr_t; +typedef uint64_t mach_vm_address_t; +typedef uint32_t mach_vm_range_flavor_t; +// type mach_vm_range_recipes_raw_t = array[*:1024] of uint8_t; +typedef uint8_t mach_vm_range_recipes_raw_t[1024]; +typedef int kern_return_t; +typedef int boolean_t; +typedef int vm_prot_t; +typedef unsigned int natural_t; +typedef natural_t mach_msg_size_t; +typedef natural_t mach_msg_size_t; +typedef unsigned int mach_msg_priority_t; +typedef natural_t mach_msg_timeout_t; +typedef natural_t mach_port_right_t; +typedef int mach_port_delta_t; +typedef int integer_t; +typedef integer_t mach_msg_option_t; +typedef kern_return_t mach_msg_return_t; + +enum MachMsgReturnT { + MACH_MSG_SUCCESS = 0x00000000, + MACH_MSG_MASK = 0x00003e00, + MACH_MSG_IPC_SPACE = 0x00002000, + MACH_MSG_VM_SPACE = 0x00001000, + MACH_MSG_IPC_KERNEL = 0x00000800, + MACH_MSG_VM_KERNEL = 0x00000400, + MACH_SEND_IN_PROGRESS = 0x10000001, + MACH_SEND_INVALID_DATA = 0x10000002, + MACH_SEND_INVALID_DEST = 0x10000003, + MACH_SEND_TIMED_OUT = 0x10000004, + MACH_SEND_INVALID_VOUCHER = 0x10000005, + MACH_SEND_INTERRUPTED = 0x10000007, + MACH_SEND_MSG_TOO_SMALL = 0x10000008, + MACH_SEND_INVALID_REPLY = 0x10000009, + MACH_SEND_INVALID_RIGHT = 0x1000000a, + MACH_SEND_INVALID_NOTIFY = 0x1000000b, + MACH_SEND_INVALID_MEMORY = 0x1000000c, + MACH_SEND_NO_BUFFER = 0x1000000d, + MACH_SEND_TOO_LARGE = 0x1000000e, + MACH_SEND_INVALID_TYPE = 0x1000000f, + MACH_SEND_INVALID_HEADER = 0x10000010, + MACH_SEND_INVALID_TRAILER = 0x10000011, + MACH_SEND_INVALID_CONTEXT = 0x10000012, + MACH_SEND_INVALID_OPTIONS = 0x10000013, + MACH_SEND_INVALID_RT_OOL_SIZE = 0x10000015, + MACH_SEND_NO_GRANT_DEST = 0x10000016, + MACH_SEND_MSG_FILTERED = 0x10000017, + MACH_SEND_AUX_TOO_SMALL = 0x10000018, + MACH_SEND_AUX_TOO_LARGE = 0x10000019, + MACH_RCV_IN_PROGRESS = 0x10004001, + MACH_RCV_INVALID_NAME = 0x10004002, + MACH_RCV_TIMED_OUT = 0x10004003, + MACH_RCV_TOO_LARGE = 0x10004004, + MACH_RCV_INTERRUPTED = 0x10004005, + MACH_RCV_PORT_CHANGED = 0x10004006, + MACH_RCV_INVALID_NOTIFY = 0x10004007, + MACH_RCV_INVALID_DATA = 0x10004008, + MACH_RCV_PORT_DIED = 0x10004009, + MACH_RCV_IN_SET = 0x1000400a, + MACH_RCV_HEADER_ERROR = 0x1000400b, + MACH_RCV_BODY_ERROR = 0x1000400c, + MACH_RCV_INVALID_TYPE = 0x1000400d, + MACH_RCV_SCATTER_SMALL = 0x1000400e, + MACH_RCV_INVALID_TRAILER = 0x1000400f, + MACH_RCV_IN_PROGRESS_TIMED = 0x10004011, + MACH_RCV_INVALID_REPLY = 0x10004012, + MACH_RCV_INVALID_ARGUMENTS = 0x10004013, +}; + +typedef uint64_t mach_msg_option64_t; + +// typedef enum : uint64_t { +// MACH64_MSG_OPTION_NONE = 0x0ull, +// /* share lower 32 bits with mach_msg_option_t */ +// MACH64_SEND_MSG = MACH_SEND_MSG, +// MACH64_RCV_MSG = MACH_RCV_MSG, + +// MACH64_RCV_LARGE = MACH_RCV_LARGE, +// MACH64_RCV_LARGE_IDENTITY = MACH_RCV_LARGE_IDENTITY, + +// MACH64_SEND_TIMEOUT = MACH_SEND_TIMEOUT, +// MACH64_SEND_OVERRIDE = MACH_SEND_OVERRIDE, +// MACH64_SEND_INTERRUPT = MACH_SEND_INTERRUPT, +// MACH64_SEND_NOTIFY = MACH_SEND_NOTIFY, +// #if KERNEL +// MACH64_SEND_ALWAYS = MACH_SEND_ALWAYS, +// MACH64_SEND_IMPORTANCE = MACH_SEND_IMPORTANCE, +// MACH64_SEND_KERNEL = MACH_SEND_KERNEL, +// #endif +// MACH64_SEND_FILTER_NONFATAL = MACH_SEND_FILTER_NONFATAL, +// MACH64_SEND_TRAILER = MACH_SEND_TRAILER, +// MACH64_SEND_NOIMPORTANCE = MACH_SEND_NOIMPORTANCE, +// MACH64_SEND_NODENAP = MACH_SEND_NODENAP, +// MACH64_SEND_SYNC_OVERRIDE = MACH_SEND_SYNC_OVERRIDE, +// MACH64_SEND_PROPAGATE_QOS = MACH_SEND_PROPAGATE_QOS, + +// MACH64_SEND_SYNC_BOOTSTRAP_CHECKIN = MACH_SEND_SYNC_BOOTSTRAP_CHECKIN, + +// MACH64_RCV_TIMEOUT = MACH_RCV_TIMEOUT, + +// MACH64_RCV_INTERRUPT = MACH_RCV_INTERRUPT, +// MACH64_RCV_VOUCHER = MACH_RCV_VOUCHER, + +// MACH64_RCV_GUARDED_DESC = MACH_RCV_GUARDED_DESC, +// MACH64_RCV_SYNC_WAIT = MACH_RCV_SYNC_WAIT, +// MACH64_RCV_SYNC_PEEK = MACH_RCV_SYNC_PEEK, + +// MACH64_MSG_STRICT_REPLY = MACH_MSG_STRICT_REPLY, +// /* following options are 64 only */ + +// /* Send and receive message as vectors */ +// MACH64_MSG_VECTOR = 0x0000000100000000ull, +// /* The message is a kobject call */ +// MACH64_SEND_KOBJECT_CALL = 0x0000000200000000ull, +// /* The message is sent to a message queue */ +// MACH64_SEND_MQ_CALL = 0x0000000400000000ull, +// /* This message destination is unknown. Used by old simulators only. */ +// MACH64_SEND_ANY = 0x0000000800000000ull, + +// #ifdef XNU_KERNEL_PRIVATE +// /* +// * If kmsg has auxiliary data, append it immediate after the message +// * and trailer. +// * +// * Must be used in conjunction with MACH64_MSG_VECTOR +// */ +// MACH64_RCV_LINEAR_VECTOR = 0x1000000000000000ull, +// /* Receive into highest addr of buffer */ +// MACH64_RCV_STACK = 0x2000000000000000ull, +// /* +// * This internal-only flag is intended for use by a single thread per-port/set! +// * If more than one thread attempts to MACH64_PEEK_MSG on a port or set, one of +// * the threads may miss messages (in fact, it may never wake up). +// */ +// MACH64_PEEK_MSG = 0x4000000000000000ull, +// /* +// * This is a mach_msg2() send/receive operation. +// */ +// MACH64_MACH_MSG2 = 0x8000000000000000ull +// #endif +// } mach_msg_option64_t; + +typedef unsigned int mach_msg_type_name_t; +typedef uint8_t *mach_voucher_attr_raw_recipe_array_t; +typedef uint8_t *mach_voucher_attr_raw_recipe_t; +typedef uint32_t mach_voucher_attr_key_t; +typedef uint32_t mach_voucher_attr_command_t; +typedef struct thread *thread_t; +typedef int vm_machine_attribute_t; +typedef int vm_machine_attribute_val_t; +typedef int vm_region_flavor_t; +typedef int vm_page_info_flavor_t; +typedef struct task *task_t; +typedef int ast_t; +typedef void *event_t; +typedef int wait_interrupt_t; +typedef integer_t mach_msg_id_t; +typedef natural_t mach_port_mscount_t; /* make-send count */ + +typedef int wait_result_t; +typedef void (*thread_continue_t)(void *, wait_result_t); +thread_t CreateWaitingThread(task_t task); +void ClearTaskWait(task_t task); + +#endif /* OSFMK_API_TYPES_H_ */ diff --git a/fuzz/xnu/osfmk/fakes/atomic.c b/fuzz/xnu/osfmk/fakes/atomic.c new file mode 100644 index 0000000..8fe347e --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/atomic.c @@ -0,0 +1,85 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/xnu/osfmk/fakes/atomic.h" + +#include "fuzz/host/hypercall/hypercall.h" + +uint64_t atomic_load_explicit_wrapper_32(uint32_t *ptr, memory_order order) { + uint64_t value = *ptr; + GetHypercallInterface()->Yield(); + return value; +} + +uint64_t atomic_load_explicit_wrapper_64(uint64_t *ptr, memory_order order) { + uint64_t value = *ptr; + GetHypercallInterface()->Yield(); + return value; +} + +int atomic_compare_exchange_weak_explicit_wrapper_32(uint32_t *p, + uint32_t *expected, + uint32_t desired, + memory_order success, + memory_order failure) { + GetHypercallInterface()->Yield(); + + if (*p == *expected) { + *p = desired; + return 1; + } + + *expected = *p; + return 0; + } + +int atomic_compare_exchange_weak_explicit_wrapper_64(uint64_t *p, + uint64_t *expected, + uint64_t desired, + memory_order success, + memory_order failure) { + GetHypercallInterface()->Yield(); + + if (*p == *expected) { + *p = desired; + return 1; + } + + *expected = *p; + return 0; +} + +void atomic_store_explicit_wrapper_32(uint32_t *ptr, uint32_t value, memory_order order) { + *ptr = value; + GetHypercallInterface()->Yield(); +} + +void atomic_store_explicit_wrapper_64(uint64_t *ptr, void* value, memory_order order) { + *ptr = (uint64_t)value; // Cast pointer to integer + GetHypercallInterface()->Yield(); +} diff --git a/fuzz/xnu/osfmk/fakes/atomic.h b/fuzz/xnu/osfmk/fakes/atomic.h new file mode 100644 index 0000000..9602f73 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/atomic.h @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_XNU_OSFMK_FAKES_ATOMIC_H_ +#define FUZZ_XNU_OSFMK_FAKES_ATOMIC_H_ + +#include +#include + +uint64_t atomic_load_explicit_wrapper_32(uint32_t *ptr, memory_order order); + +uint64_t atomic_load_explicit_wrapper_64(uint64_t *ptr, memory_order order); + +// Should return bool but we don't want to export that since stdbool.h conflicts with XNU boolean_t +int atomic_compare_exchange_weak_explicit_wrapper_32(uint32_t *p, + uint32_t *expected, + uint32_t desired, + memory_order success, + memory_order failure); + +int atomic_compare_exchange_weak_explicit_wrapper_64(uint64_t *p, + uint64_t *expected, + uint64_t desired, + memory_order success, + memory_order failure); + +void atomic_store_explicit_wrapper_32(uint32_t *ptr, uint32_t value, memory_order order); + +void atomic_store_explicit_wrapper_64(uint64_t *ptr, void* value, memory_order order); + +#endif // FUZZ_XNU_OSFMK_FAKES_ATOMIC_H_ diff --git a/fuzz/xnu/osfmk/fakes/clock.c b/fuzz/xnu/osfmk/fakes/clock.c new file mode 100644 index 0000000..86c7c9b --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/clock.c @@ -0,0 +1,130 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void absolutetime_to_nanoseconds(uint64_t abstime, uint64_t *result) { + *result = abstime; +} + +void clock_interval_to_absolutetime_interval(uint32_t interval, + uint32_t scale_factor, + uint64_t *result) { + uint64_t nanosecs = (uint64_t)interval * scale_factor; + uint64_t t64; + + *result = (t64 = nanosecs / NSEC_PER_SEC) * 1; + nanosecs -= (t64 * NSEC_PER_SEC); + *result += (nanosecs * 1) / NSEC_PER_SEC; +} + +void absolutetime_to_microtime(uint64_t abstime, clock_sec_t *secs, + clock_usec_t *microsecs) { + uint64_t t64; + + *secs = t64 = abstime / 1; + abstime -= (t64 * 1); + + *microsecs = (uint32_t)(abstime / 1); +} + +void clock_get_system_microtime(clock_sec_t *secs, clock_usec_t *microsecs) { + absolutetime_to_microtime(mach_absolute_time(), secs, microsecs); +} + +void clock_get_system_nanotime(clock_sec_t *secs, clock_nsec_t *nanosecs) { + uint64_t abstime; + uint64_t t64; + + abstime = mach_absolute_time(); + *secs = (t64 = abstime / 1); + abstime -= (t64 * 1); + + *nanosecs = (clock_nsec_t)((abstime * NSEC_PER_SEC) / 1); +} + +uint64_t g_abstime; +uint64_t g_sec; +uint64_t g_frac; +uint64_t g_scale; +uint64_t g_tick_per_sec; + +void commpage_set_timestamp(uint64_t abstime, uint64_t sec, uint64_t frac, + uint64_t scale, uint64_t tick_per_sec) { + g_abstime = abstime; + g_sec = sec; + g_frac = frac; + g_scale = scale; + g_tick_per_sec = tick_per_sec; +} + +void clock_gettimeofday_set_commpage(uint64_t abstime, uint64_t sec, + uint64_t frac, uint64_t scale, + uint64_t tick_per_sec) { + commpage_set_timestamp(abstime, sec, frac, scale, tick_per_sec); +} + +void PESetUTCTimeOfDay(clock_sec_t secs, clock_usec_t usecs) {} + +void commpage_update_boottime(uint64_t boottime_usec) {} + +void PEGetUTCTimeOfDay(clock_sec_t *secs, clock_usec_t *usecs) { + GetHypercallInterface()->Abort(); +} + +void commpage_update_mach_continuous_time(uint64_t sleeptime) { + GetHypercallInterface()->Abort(); +} + +boolean_t ml_delay_should_spin(uint64_t interval) { + GetHypercallInterface()->Abort(); +} + +void machine_delay_until(uint64_t interval, uint64_t deadline) { +#pragma unused(interval) + uint64_t now; + + do { + GetHypercallInterface()->Yield(); + // __builtin_arm_wfe(); + now = mach_absolute_time(); + } while (now < deadline); +} + +void rtclock_init() {} + +// TODO(nedwill): implement these functions to support time passage +uint64_t mach_absolute_time() { + return 0; +} + +uint64_t mach_approximate_time() { + return 1337; +} diff --git a/fuzz/xnu/osfmk/fakes/continuation.c b/fuzz/xnu/osfmk/fakes/continuation.c new file mode 100644 index 0000000..5cdc378 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/continuation.c @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/hypercall/hypercall.h" + +void unix_syscall_return() { + GetHypercallInterface()->SyscallReturn(); +} + +__attribute__((noreturn)) void thread_syscall_return(int rv) { + GetHypercallInterface()->SyscallReturn(); + __builtin_unreachable(); +} + +__attribute__((noreturn)) +void thread_exception_return() { + GetHypercallInterface()->SyscallReturn(); +} diff --git a/fuzz/xnu/osfmk/fakes/copyio.c b/fuzz/xnu/osfmk/fakes/copyio.c new file mode 100644 index 0000000..96210e7 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/copyio.c @@ -0,0 +1,109 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void *malloc(size_t size); +void free(void *ptr); + +int mincore(void *addr, size_t length, unsigned char *vec); + +#undef copyin +int __attribute__((warn_unused_result)) +copyin(const user_addr_t uaddr, void *kaddr, size_t len) { + // Address 1 means use fuzzed bytes, otherwise use real bytes. + // NOTE: this does not support nested useraddr. + // TODO(nedwill): disable for now when we have random syscalls + // TODO(nedwill): just disable copyin for unsupport syscalls from the + // fuzz target instead of checking every time + // bool mapped = HostIsMapped(uaddr, len); + + if (!GetHypercallInterface()->IsCopyioBlocked() && uaddr != 1) { + // GetHypercallInterface()->ThreadPrintf("copyin doing real copy from %p\n", uaddr); + memcpy(kaddr, (void *)uaddr, len); + return 0; + } + + if (!GetHypercallInterface()->GetRemainingFuzzedBytes() || GetHypercallInterface()->GetFuzzedBool()) { + return -1; + } + + GetHypercallInterface()->GetFuzzedBytes(kaddr, len); + return 0; +} + +int copyinstr(const user_addr_t user_addr, char *kernel_addr, vm_size_t max, + vm_size_t *actual) { + // TODO(nedwill): disable for now when we have random syscalls + // if (user_addr != 1) { + // strncpy(kernel_addr, (void *)user_addr, max); + // return 0; + // } + + if (!GetHypercallInterface()->GetRemainingFuzzedBytes() || GetHypercallInterface()->GetFuzzedBool()) { + return -1; + } + + GetHypercallInterface()->GetFuzzedBytes(kernel_addr, max); + if (max) { + kernel_addr[max - 1] = 0; + } + return 0; +} + +#undef copyout +int copyout(const void *kaddr, user_addr_t udaddr, size_t len) { + if (GetHypercallInterface()->GetFuzzedBool()) { + return 1; + } + + if (!udaddr || udaddr == 1 || GetHypercallInterface()->IsCopyioBlocked()) { + void *buf = malloc(len); + memcpy(buf, kaddr, len); + free(buf); + return 0; + } + + memcpy((void *)udaddr, kaddr, len); + return 0; +} + +int copyin_atomic32(const user_addr_t user_addr, uint32_t *kernel_addr) { + return copyin(user_addr, kernel_addr, sizeof(uint32_t)); +} + +int copyin_atomic64(const user_addr_t user_addr, uint64_t *kernel_addr) { + return copyin(user_addr, kernel_addr, sizeof(uint64_t)); +} diff --git a/fuzz/xnu/osfmk/fakes/cpu_data.c b/fuzz/xnu/osfmk/fakes/cpu_data.c new file mode 100644 index 0000000..403f898 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/cpu_data.c @@ -0,0 +1,61 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "BUILD/obj/EXPORT_HDRS/osfmk/i386/cpu_data.h" + +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +static struct processor processor; + +static cpu_data_t cpu_data = {.cpu_processor = &processor}; + +cpu_data_t *cpu_data_ptr[64] = {&cpu_data}; + +void machine_set_current_thread(thread_t thread) { + GetHypercallInterface()->SetCurrentThreadT(thread); +} + +extern thread_t bootstrap_thread; + +#undef current_thread +// Make visible for BSD half +thread_t current_thread() { + return GetHypercallInterface()->GetCurrentThreadT(); +} + +processor_t current_processor(void) { + return cpu_data.cpu_processor; +} + +cpu_threadtype_t cpu_threadtype() { + return CPU_THREADTYPE_NONE; +} diff --git a/fuzz/xnu/osfmk/fakes/locks.c b/fuzz/xnu/osfmk/fakes/locks.c new file mode 100644 index 0000000..56f3234 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/locks.c @@ -0,0 +1,525 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +static timer_coalescing_priority_params_ns_t priority_params; +timer_coalescing_priority_params_ns_t *timer_call_get_priority_params() { + return &priority_params; +} + +wait_result_t lck_spin_sleep_grp(lck_spin_t *lck, + lck_sleep_action_t lck_sleep_action, + event_t event, wait_interrupt_t interruptible, + lck_grp_t *grp) { + wait_result_t res = 0; + + if ((lck_sleep_action & ~LCK_SLEEP_MASK) != 0) { + panic("Invalid lock sleep action %x\n", lck_sleep_action); + } + + res = assert_wait(event, interruptible); + if (res == THREAD_WAITING) { + lck_spin_unlock(lck); + res = thread_block(THREAD_CONTINUE_NULL); + if (!(lck_sleep_action & LCK_SLEEP_UNLOCK)) { + lck_spin_lock_grp(lck, grp); + } + } else if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + lck_spin_unlock(lck); + } + + return res; +} + +wait_result_t lck_spin_sleep(lck_spin_t *lck, + lck_sleep_action_t lck_sleep_action, event_t event, + wait_interrupt_t interruptible) { + return lck_spin_sleep_grp(lck, lck_sleep_action, event, interruptible, + LCK_GRP_NULL); +} + +wait_result_t lck_spin_sleep_deadline(lck_spin_t *lck, + lck_sleep_action_t lck_sleep_action, + event_t event, + wait_interrupt_t interruptible, + uint64_t deadline) { + wait_result_t res; + + if ((lck_sleep_action & ~LCK_SLEEP_MASK) != 0) { + panic("Invalid lock sleep action %x", lck_sleep_action); + } + + res = assert_wait_deadline(event, interruptible, deadline); + if (res == THREAD_WAITING) { + lck_spin_unlock(lck); + res = thread_block(THREAD_CONTINUE_NULL); + if (!(lck_sleep_action & LCK_SLEEP_UNLOCK)) { + lck_spin_lock(lck); + } + } else if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + lck_spin_unlock(lck); + } + + return res; +} + +static wait_result_t sleep_with_inheritor_and_turnstile( + event_t event, thread_t inheritor, wait_interrupt_t interruptible, + uint64_t deadline, void (^primitive_lock)(void), + void (^primitive_unlock)(void)) { + turnstile_type_t type = TURNSTILE_SLEEP_INHERITOR; + wait_result_t ret; + uint32_t index; + struct turnstile *ts = NULL; + + /* + * the hash bucket spinlock is used as turnstile interlock, + * lock it before releasing the primitive lock + */ + turnstile_hash_bucket_lock((uintptr_t)event, &index, type); + + primitive_unlock(); + + ts = turnstile_prepare_hash((uintptr_t)event, type); + + thread_set_pending_block_hint(current_thread(), + kThreadWaitSleepWithInheritor); + /* + * We need TURNSTILE_DELAYED_UPDATE because we will call + * waitq_assert_wait64 after. + */ + turnstile_update_inheritor( + ts, inheritor, (TURNSTILE_DELAYED_UPDATE | TURNSTILE_INHERITOR_THREAD)); + + ret = waitq_assert_wait64(&ts->ts_waitq, CAST_EVENT64_T(event), interruptible, + deadline); + + turnstile_hash_bucket_unlock((uintptr_t)NULL, &index, type, 0); + + /* + * Update new and old inheritor chains outside the interlock; + */ + turnstile_update_inheritor_complete(ts, TURNSTILE_INTERLOCK_NOT_HELD); + + if (ret == THREAD_WAITING) { + ret = thread_block(THREAD_CONTINUE_NULL); + } + + turnstile_hash_bucket_lock((uintptr_t)NULL, &index, type); + + turnstile_complete_hash((uintptr_t)event, type); + + turnstile_hash_bucket_unlock((uintptr_t)NULL, &index, type, 0); + + turnstile_cleanup(); + + primitive_lock(); + + return ret; +} + +/* + * Name: lck_spin_sleep_with_inheritor + * + * Description: deschedule the current thread and wait on the waitq associated with event to be woken up. + * While waiting, the sched priority of the waiting thread will contribute to the push of the event that will + * be directed to the inheritor specified. + * An interruptible mode and deadline can be specified to return earlier from the wait. + * + * Args: + * Arg1: lck_spin_t lock used to protect the sleep. The lock will be dropped while sleeping and reaquired before returning according to the sleep action specified. + * Arg2: sleep action. LCK_SLEEP_DEFAULT, LCK_SLEEP_UNLOCK. + * Arg3: event to wait on. + * Arg4: thread to propagate the event push to. + * Arg5: interruptible flag for wait. + * Arg6: deadline for wait. + * + * Conditions: Lock must be held. Returns with the lock held according to the sleep action specified. + * Lock will be dropped while waiting. + * The inheritor specified cannot return to user space or exit until another inheritor is specified for the event or a + * wakeup for the event is called. + * + * Returns: result of the wait. + */ +wait_result_t lck_spin_sleep_with_inheritor(lck_spin_t *lock, + lck_sleep_action_t lck_sleep_action, + event_t event, thread_t inheritor, + wait_interrupt_t interruptible, + uint64_t deadline) { + if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + return sleep_with_inheritor_and_turnstile(event, inheritor, interruptible, + deadline, + ^{ + }, + ^{ + lck_spin_unlock(lock); + }); + } else { + return sleep_with_inheritor_and_turnstile( + event, inheritor, interruptible, deadline, + ^{ + lck_spin_lock(lock); + }, + ^{ + lck_spin_unlock(lock); + }); + } +} + +wait_result_t lck_ticket_sleep_with_inheritor( + lck_ticket_t *lock, lck_grp_t *grp, lck_sleep_action_t lck_sleep_action, + event_t event, thread_t inheritor, wait_interrupt_t interruptible, + uint64_t deadline) { + if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + return sleep_with_inheritor_and_turnstile(event, inheritor, interruptible, + deadline, + ^{ + }, + ^{ + lck_ticket_unlock(lock); + }); + } else { + return sleep_with_inheritor_and_turnstile( + event, inheritor, interruptible, deadline, + ^{ + lck_ticket_lock(lock, grp); + }, + ^{ + lck_ticket_unlock(lock); + }); + } +} + +wait_result_t lck_rw_sleep(lck_rw_t *lck, lck_sleep_action_t lck_sleep_action, + event_t event, wait_interrupt_t interruptible) { + wait_result_t res = 0; + lck_rw_type_t lck_rw_type = 0; + thread_pri_floor_t token; + + if ((lck_sleep_action & ~LCK_SLEEP_MASK) != 0) { + panic("Invalid lock sleep action %x", lck_sleep_action); + } + + if (lck_sleep_action & LCK_SLEEP_PROMOTED_PRI) { + /* + * Although we are dropping the RW lock, the intent in most cases + * is that this thread remains as an observer, since it may hold + * some secondary resource, but must yield to avoid deadlock. In + * this situation, make sure that the thread is boosted to the + * ceiling while blocked, so that it can re-acquire the + * RW lock at that priority. + */ + token = thread_priority_floor_start(); + } + + res = assert_wait(event, interruptible); + if (res == THREAD_WAITING) { + lck_rw_type = lck_rw_done(lck); + res = thread_block(THREAD_CONTINUE_NULL); + if (!(lck_sleep_action & LCK_SLEEP_UNLOCK)) { + if (!(lck_sleep_action & (LCK_SLEEP_SHARED | LCK_SLEEP_EXCLUSIVE))) { + lck_rw_lock(lck, lck_rw_type); + } else if (lck_sleep_action & LCK_SLEEP_EXCLUSIVE) { + lck_rw_lock_exclusive(lck); + } else { + lck_rw_lock_shared(lck); + } + } + } else if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + (void)lck_rw_done(lck); + } + + if (lck_sleep_action & LCK_SLEEP_PROMOTED_PRI) { + thread_priority_floor_end(&token); + } + + return res; +} + +wait_result_t lck_mtx_sleep(lck_mtx_t *lck, lck_sleep_action_t lck_sleep_action, + event_t event, wait_interrupt_t interruptible) { + wait_result_t res = 0; + thread_pri_floor_t token; + + KERNEL_DEBUG( + MACHDBG_CODE(DBG_MACH_LOCKS, LCK_MTX_SLEEP_CODE) | DBG_FUNC_START, + VM_KERNEL_UNSLIDE_OR_PERM(lck), (int)lck_sleep_action, + VM_KERNEL_UNSLIDE_OR_PERM(event), (int)interruptible, 0); + + if ((lck_sleep_action & ~LCK_SLEEP_MASK) != 0) { + panic("Invalid lock sleep action %x", lck_sleep_action); + } + + if (lck_sleep_action & LCK_SLEEP_PROMOTED_PRI) { + /* + * We get a priority floor + * during the time that this thread is asleep, so that when it + * is re-awakened (and not yet contending on the mutex), it is + * runnable at a reasonably high priority. + */ + token = thread_priority_floor_start(); + } + + res = assert_wait(event, interruptible); + if (res == THREAD_WAITING) { + lck_mtx_unlock(lck); + res = thread_block(THREAD_CONTINUE_NULL); + if (!(lck_sleep_action & LCK_SLEEP_UNLOCK)) { + if ((lck_sleep_action & LCK_SLEEP_SPIN)) { + lck_mtx_lock_spin(lck); + } else if ((lck_sleep_action & LCK_SLEEP_SPIN_ALWAYS)) { + lck_mtx_lock_spin_always(lck); + } else { + lck_mtx_lock(lck); + } + } + } else if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + lck_mtx_unlock(lck); + } + + if (lck_sleep_action & LCK_SLEEP_PROMOTED_PRI) { + thread_priority_floor_end(&token); + } + + KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS, LCK_MTX_SLEEP_CODE) | DBG_FUNC_END, + (int)res, 0, 0, 0, 0); + + return res; +} + +wait_result_t lck_mtx_sleep_deadline(lck_mtx_t *lck, + lck_sleep_action_t lck_sleep_action, + event_t event, + wait_interrupt_t interruptible, + uint64_t deadline) { + wait_result_t res = 0; + thread_pri_floor_t token; + + KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS, LCK_MTX_SLEEP_DEADLINE_CODE) | + DBG_FUNC_START, + VM_KERNEL_UNSLIDE_OR_PERM(lck), (int)lck_sleep_action, + VM_KERNEL_UNSLIDE_OR_PERM(event), (int)interruptible, 0); + + if ((lck_sleep_action & ~LCK_SLEEP_MASK) != 0) { + panic("Invalid lock sleep action %x", lck_sleep_action); + } + + if (lck_sleep_action & LCK_SLEEP_PROMOTED_PRI) { + /* + * See lck_mtx_sleep(). + */ + token = thread_priority_floor_start(); + } + + res = assert_wait_deadline(event, interruptible, deadline); + if (res == THREAD_WAITING) { + lck_mtx_unlock(lck); + res = thread_block(THREAD_CONTINUE_NULL); + if (!(lck_sleep_action & LCK_SLEEP_UNLOCK)) { + if ((lck_sleep_action & LCK_SLEEP_SPIN)) { + lck_mtx_lock_spin(lck); + } else { + lck_mtx_lock(lck); + } + } + } else if (lck_sleep_action & LCK_SLEEP_UNLOCK) { + lck_mtx_unlock(lck); + } + + if (lck_sleep_action & LCK_SLEEP_PROMOTED_PRI) { + thread_priority_floor_end(&token); + } + + KERNEL_DEBUG( + MACHDBG_CODE(DBG_MACH_LOCKS, LCK_MTX_SLEEP_DEADLINE_CODE) | DBG_FUNC_END, + (int)res, 0, 0, 0, 0); + + return res; +} + +TUNABLE(uint32_t, LcksOpts, "lcks", 0); + +uint64_t hw_wait_while_equals64(uint64_t *address, uint64_t current) { + GetHypercallInterface()->Abort(); +} + +bool lck_ticket_lock_try(lck_ticket_t *tlock, lck_grp_t *grp) { + GetHypercallInterface()->Abort(); +} + +#undef hw_lck_ticket_reserve +bool hw_lck_ticket_reserve(hw_lck_ticket_t *tlock, + uint32_t *ticket LCK_GRP_ARG(lck_grp_t *grp)) { + // TODO(nedwill): only permit invalid locks in the allow_invalid case + bool got_lock = hw_lck_ticket_lock_try(tlock, NULL); + if (got_lock) { + return true; + } + + *ticket = 0; + return false; +} + +// #undef hw_lck_ticket_wait +hw_lock_status_t hw_lck_ticket_wait(hw_lck_ticket_t *tlock, uint32_t ticket, + hw_spin_policy_t policy, lck_grp_t *grp) { + return hw_lck_ticket_lock_to(tlock, 0, NULL); +} + +#undef hw_lck_ticket_reserve_allow_invalid +hw_lock_status_t hw_lck_ticket_reserve_allow_invalid( + hw_lck_ticket_t *tlock, uint32_t *ticket LCK_GRP_ARG(lck_grp_t *grp)) { + return hw_lck_ticket_reserve(tlock, ticket); +} + +void lck_ticket_startup_init(struct lck_ticket_startup_spec *spec) {} + +static kern_return_t wakeup_with_inheritor_and_turnstile( + event_t event, wait_result_t result, bool wake_one, + lck_wake_action_t action, thread_t *thread_wokenup) { + turnstile_type_t type = TURNSTILE_SLEEP_INHERITOR; + uint32_t index; + struct turnstile *ts = NULL; + kern_return_t ret = KERN_NOT_WAITING; + thread_t wokeup; + + /* + * the hash bucket spinlock is used as turnstile interlock + */ + turnstile_hash_bucket_lock((uintptr_t)event, &index, type); + + ts = turnstile_prepare_hash((uintptr_t)event, type); + + if (wake_one) { + waitq_wakeup_flags_t flags = WAITQ_WAKEUP_DEFAULT; + + if (action == LCK_WAKE_DEFAULT) { + flags = WAITQ_UPDATE_INHERITOR; + } else { + assert(action == LCK_WAKE_DO_NOT_TRANSFER_PUSH); + } + + /* + * WAITQ_UPDATE_INHERITOR will call turnstile_update_inheritor + * if it finds a thread + */ + wokeup = waitq_wakeup64_identify(&ts->ts_waitq, CAST_EVENT64_T(event), + result, flags); + if (wokeup != NULL) { + if (thread_wokenup != NULL) { + *thread_wokenup = wokeup; + } else { + thread_deallocate_safe(wokeup); + } + ret = KERN_SUCCESS; + if (action == LCK_WAKE_DO_NOT_TRANSFER_PUSH) { + goto complete; + } + } else { + if (thread_wokenup != NULL) { + *thread_wokenup = NULL; + } + turnstile_update_inheritor(ts, TURNSTILE_INHERITOR_NULL, + TURNSTILE_IMMEDIATE_UPDATE); + ret = KERN_NOT_WAITING; + } + } else { + ret = waitq_wakeup64_all(&ts->ts_waitq, CAST_EVENT64_T(event), result, + WAITQ_UPDATE_INHERITOR); + } + + /* + * turnstile_update_inheritor_complete could be called while holding the interlock. + * In this case the new inheritor or is null, or is a thread that is just been woken up + * and have not blocked because it is racing with the same interlock used here + * after the wait. + * So there is no chain to update for the new inheritor. + * + * However unless the current thread is the old inheritor, + * old inheritor can be blocked and requires a chain update. + * + * The chain should be short because kernel turnstiles cannot have user turnstiles + * chained after them. + * + * We can anyway optimize this by asking turnstile to tell us + * if old inheritor needs an update and drop the lock + * just in that case. + */ + turnstile_hash_bucket_unlock((uintptr_t)NULL, &index, type, 0); + + turnstile_update_inheritor_complete(ts, TURNSTILE_INTERLOCK_NOT_HELD); + + turnstile_hash_bucket_lock((uintptr_t)NULL, &index, type); + +complete: + turnstile_complete_hash((uintptr_t)event, type); + + turnstile_hash_bucket_unlock((uintptr_t)NULL, &index, type, 0); + + turnstile_cleanup(); + + return ret; +} + +kern_return_t wakeup_one_with_inheritor(event_t event, wait_result_t result, + lck_wake_action_t action, + thread_t *thread_wokenup) { + return wakeup_with_inheritor_and_turnstile(event, result, TRUE, action, + thread_wokenup); +} + +kern_return_t wakeup_all_with_inheritor(event_t event, wait_result_t result) { + return wakeup_with_inheritor_and_turnstile(event, result, FALSE, 0, NULL); +} diff --git a/fuzz/xnu/osfmk/fakes/log.c b/fuzz/xnu/osfmk/fakes/log.c new file mode 100644 index 0000000..3e08aea --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/log.c @@ -0,0 +1,66 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/hypercall/hypercall.h" + +__attribute__((noinline, not_tail_called)) void _os_log_internal( + void *dso, int log, uint8_t type, const char *message, ...) { + if (!is_verbose) { + return; + } + va_list args; + va_start(args, message); + GetHypercallInterface()->LogVaList(message, args); + va_end(args); +} + +void kprintf(const char *fmt, ...) { + if (!is_verbose) { + return; + } + va_list args; + va_start(args, fmt); + GetHypercallInterface()->LogVaList(fmt, args); + va_end(args); +} + +int printf_if_verbose(const char *format, ...) { + if (!is_verbose) { + return 0; + } + va_list args; + va_start(args, format); + GetHypercallInterface()->LogVaList(format, args); + va_end(args); + return 0; +} + +void os_log_with_args(void *oslog, int type, const char *format, va_list args, + void *ret_addr) { + GetHypercallInterface()->LogVaList(format, args); +} diff --git a/fuzz/xnu/osfmk/fakes/mach_notify.c b/fuzz/xnu/osfmk/fakes/mach_notify.c new file mode 100644 index 0000000..c228eb8 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/mach_notify.c @@ -0,0 +1,80 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +// Nothing locked. Consumes a ref/soright for port. +// Consumes a ref for right. +kern_return_t mach_notify_port_destroyed(mach_port_t notify, + mach_port_t rights) { + notify->ip_sorights--; + ip_release(notify); + ip_release(rights); + return KERN_SUCCESS; +} + +kern_return_t mach_notify_send_once(mach_port_t notify) { + notify->ip_sorights--; + ip_release(notify); + return KERN_SUCCESS; +} + +kern_return_t mach_notify_port_deleted(mach_port_t notify, + mach_port_name_t name) { + (void)name; + notify->ip_sorights--; + ip_release(notify); + return KERN_SUCCESS; +} + +kern_return_t mach_notify_send_possible(mach_port_t notify, + mach_port_name_t name) { + (void)name; + notify->ip_sorights--; + ip_release(notify); + return KERN_SUCCESS; +} + +kern_return_t mach_notify_no_senders(mach_port_t notify, + mach_port_mscount_t mscount) { + (void)mscount; + notify->ip_sorights--; + ip_release(notify); + return KERN_SUCCESS; +} + +kern_return_t mach_notify_dead_name(mach_port_t notify, mach_port_name_t name) { + (void)name; + notify->ip_sorights--; + ip_release(notify); + return KERN_SUCCESS; +} diff --git a/fuzz/fakes/thread.c b/fuzz/xnu/osfmk/fakes/machine.c similarity index 74% rename from fuzz/fakes/thread.c rename to fuzz/xnu/osfmk/fakes/machine.c index 229f2c1..5b77866 100644 --- a/fuzz/fakes/thread.c +++ b/fuzz/xnu/osfmk/fakes/machine.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 Google LLC + * Copyright 2024 Google LLC * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -26,24 +26,16 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -// #include -#include - -#include "osfmk/mach/kern_return.h" +#include "third_party/xnu/osfmk/kern/machine.h" -typedef void* thread_t; - -thread_t current_thread() { return NULL; } +#include -kern_return_t thread_policy_set() { - return KERN_SUCCESS; +char *machine_boot_info(char *buf, vm_size_t buf_len) { + return buf; } -// TODO: mock data here -char fake_thread[4096]; - -// let caller read/write to fake thread -void* get_bsdthread_info(thread_t thread) { return fake_thread; } - -// callbacks -void* thread_call_allocate() { return (void*)1; } +thread_t machine_processor_shutdown(thread_t thread, + void (*doshutdown)(processor_t), + processor_t processor) { + return NULL; +} diff --git a/fuzz/xnu/osfmk/fakes/osfmk_stubs.c b/fuzz/xnu/osfmk/fakes/osfmk_stubs.c new file mode 100644 index 0000000..604fd4f --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/osfmk_stubs.c @@ -0,0 +1,1506 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +vm_size_t mem_size = 4000000; + +uint64_t max_mem_actual = 4000000; + +int IODTGetLoaderInfo(const char *key, void **infoAddr, int *infosize) { + if (!*infosize) { + return 0; + } + GetHypercallInterface()->GetFuzzedBytes(*infoAddr, *infosize); + return 0; +} +void IORecordProgressBackbuffer() { + GetHypercallInterface()->Abort(); +} + +void IOSleep() { + GetHypercallInterface()->Yield(); +} + +bool IOTaskHasEntitlement() { + return true; +} +void Idle_PTs_release() {} +uint64_t LockTimeOutTSC = 0; +uint32_t LockTimeOutUsec = 0; +void MD5Final() { + GetHypercallInterface()->Abort(); +} +void MD5Init() { + GetHypercallInterface()->Abort(); +} +void MD5Update() { + GetHypercallInterface()->Abort(); +} +uint64_t MutexSpin = 0; +void OSBacktrace() {} +void OSKextGetAllocationSiteForCaller() { + GetHypercallInterface()->Abort(); +} +void OSKextGetKmodIDForSite() { + GetHypercallInterface()->Abort(); +} +void OSKextRemoveKextBootstrap() {} +void OSKextSetReceiptQueried() { + GetHypercallInterface()->Abort(); +} +void SavePanicInfo(const char *message, void *panic_data, + uint64_t panic_options) { + GetHypercallInterface()->Abort(); +} +void WKdm_compress_new() { + GetHypercallInterface()->Abort(); +} +void WKdm_decompress_new() { + GetHypercallInterface()->Abort(); +} + +void free(void *ptr); + +void _Block_object_dispose(const void *object, const int flags) {} + +unsigned int _MachineStateCount[] = {}; +void _NSConcreteStackBlock() { + GetHypercallInterface()->Abort(); +} +void _disable_preemption() { + GetHypercallInterface()->Abort(); +} +void _enable_preemption() { + GetHypercallInterface()->SetInterruptsEnabled(true); +} +kernel_mach_header_t _mh_execute_header; + +void abort_panic_transfer() { + GetHypercallInterface()->Abort(); +} +void allow_data_exec() { + GetHypercallInterface()->Abort(); +} +void allow_stack_exec() { + GetHypercallInterface()->Abort(); +} +void arcade_upcall() { + GetHypercallInterface()->Abort(); +} +void bcopy_phys() { + GetHypercallInterface()->Abort(); +} +void begin_panic_transfer() { + GetHypercallInterface()->Abort(); +} + +int cc_rand_generate(void *out, size_t outlen) { + memset(out, 0, outlen); + return 0; +} + +void coalition_notification() { + GetHypercallInterface()->Abort(); +} +void commpage_populate() {} +void commpage_set_memory_pressure() { + GetHypercallInterface()->Abort(); +} +void commpage_text_populate() {} +void commpage_update_active_cpus() { + GetHypercallInterface()->Abort(); +} +void commpage_update_mach_approximate_time() {} +void consider_machine_adjust() { + GetHypercallInterface()->Abort(); +} +void consider_machine_collect() { + GetHypercallInterface()->Abort(); +} +boolean_t coprocessor_paniclog_flush = false; + +int copyinmsg(const user_addr_t user_addr, char *kernel_addr, + mach_msg_size_t nbytes) { + return copyin(user_addr, kernel_addr, nbytes); +} + +int copyout_atomic32(uint32_t u32, user_addr_t user_addr) { + return copyout(&u32, user_addr, sizeof(u32)); +} + +int copyout_atomic64(uint64_t u64, user_addr_t user_addr) { + GetHypercallInterface()->Abort(); +} + +int copyoutmsg(const char *kernel_addr, user_addr_t user_addr, + mach_msg_size_t nbytes) { + return copyout(kernel_addr, user_addr, nbytes); +} + +// TODO(nedwill): no-op until we determine if these are used in a way +// that affects security +OS_OVERLOADABLE +void counter_add(counter_t *counter, uint64_t amount) {} + +OS_OVERLOADABLE +void counter_inc(counter_t *counter) {} + +OS_OVERLOADABLE +void counter_inc_preemption_disabled(counter_t *counter) {} + +void cpu_can_exit() { + GetHypercallInterface()->Abort(); +} +void cpu_control() { + GetHypercallInterface()->Abort(); +} +void cpu_info() { + GetHypercallInterface()->Abort(); +} +void cpu_info_count() {} +void cpu_start() { + GetHypercallInterface()->Abort(); +} +vm_offset_t current_percpu_base() { + return 0; +} +void debug_task() { + GetHypercallInterface()->Abort(); +} +void device_close() { + GetHypercallInterface()->Abort(); +} +void device_data_action() { + GetHypercallInterface()->Abort(); +} +bool disable_serial_output = false; + +uint64_t early_random(void) { + return 0; +} + +void exception_raise() {} +void exception_raise_state() { + GetHypercallInterface()->Abort(); +} +void exception_raise_state_identity() { + GetHypercallInterface()->Abort(); +} +void fairplayd_arcade_request() { + GetHypercallInterface()->Abort(); +} +void fillPage() {} +void gIOHibernateMode() { + GetHypercallInterface()->Abort(); +} +void gLoadedKextSummaries() { + GetHypercallInterface()->Abort(); +} +crypto_functions_t g_crypto_funcs = NULL; + + +static x86_saved_state_t state; + +void *get_user_regs(thread_t thread) { + memset(&state, 0, sizeof(state)); + GetHypercallInterface()->GetFuzzedBytes(&state, sizeof(state)); + return &state; +} + +void halt_all_cpus() {} +void hibernate_page_bitset() { + GetHypercallInterface()->Abort(); +} +void hibernate_page_bittst() { + GetHypercallInterface()->Abort(); +} +void hibernate_page_list_allocate() { + GetHypercallInterface()->Abort(); +} +void hibernate_page_list_setall_machine() { + GetHypercallInterface()->Abort(); +} +void hibernate_processor_setup() { + GetHypercallInterface()->Abort(); +} +void hibernate_should_abort() { + GetHypercallInterface()->Abort(); +} +void hibernate_vm_locks_are_safe() { + GetHypercallInterface()->Abort(); +} +void inuse_ptepages_count() { + GetHypercallInterface()->Abort(); +} +void io_map_spec() { + GetHypercallInterface()->Abort(); +} +void iokit_add_reference() { + GetHypercallInterface()->Abort(); +} +void iokit_port_object_description() { + GetHypercallInterface()->Abort(); +} +void iokit_remove_reference() { + GetHypercallInterface()->Abort(); +} +void iokit_task_app_suspended_changed() { + GetHypercallInterface()->Abort(); +} +void iokit_task_terminate() {} + +OS_OVERLOADABLE +void counter_dec(unsigned long long **out) {} + +void kasan_fakestack_drop() {} +void kasan_fakestack_gc() {} +void kasan_init_thread() {} +void kasan_late_init() {} +void kasan_notify_address() {} +void kasan_notify_address_nopoison() { + GetHypercallInterface()->Abort(); +} +void kasan_unpoison_stack() {} +void kdp_has_polled_corefile() { + GetHypercallInterface()->Abort(); +} +void kdp_polled_corefile_error() { + GetHypercallInterface()->Abort(); +} +void kdp_raise_exception() { + GetHypercallInterface()->Abort(); +} +void kdp_vtophys() { + GetHypercallInterface()->Abort(); +} +void kern_dump() { + GetHypercallInterface()->Abort(); +} +kern_return_t kernel_do_post() { + return KERN_SUCCESS; +} +kern_return_t kernel_list_tests() { + return KERN_SUCCESS; +} +void kernel_preempt_check() { + GetHypercallInterface()->Abort(); +} +void kheap_temp_leak_panic() { + GetHypercallInterface()->Abort(); +} +void kmod_panic_dump(vm_offset_t *addr, unsigned int dump_cnt) { + GetHypercallInterface()->Abort(); +} + +uint32_t kpc_configurable_config_count() { + return GetHypercallInterface()->GetFuzzedUint32(); +} + +uint32_t kpc_configurable_count() { + return 6; +} + +uint64_t kpc_configurable_max() { + return GetHypercallInterface()->GetFuzzedUint64(); +} + +uint32_t kpc_fixed_config_count() { + return GetHypercallInterface()->GetFuzzedUint32(); +} + +uint32_t kpc_fixed_count() { + return GetHypercallInterface()->GetFuzzedUint32InRange(0, 2); +} + +uint64_t kpc_fixed_max() { + return GetHypercallInterface()->GetFuzzedUint64(); +} + +uint32_t kpc_get_all_cpus_counters() { + return GetHypercallInterface()->GetFuzzedUint32InRange(0, 32); +} + +int kpc_get_configurable_config(uint64_t *configv, uint64_t pmc_mask) { + *configv = GetHypercallInterface()->GetFuzzedUint64(); + return 0; +} + +uint32_t kpc_get_configurable_counters() { + return GetHypercallInterface()->GetFuzzedUint32(); +} + +int kpc_get_fixed_config(uint64_t *a) { + *a = GetHypercallInterface()->GetFuzzedUint64(); + return 0; +} + +int kpc_get_fixed_counters() { + return 32; +} + +void kpc_get_rawpmu_config() { + GetHypercallInterface()->Abort(); +} + +boolean_t kpc_is_running_configurable() { + return GetHypercallInterface()->GetFuzzedBool(); +} + +boolean_t kpc_is_running_fixed() { + return GetHypercallInterface()->GetFuzzedBool(); +} + +uint32_t kpc_rawpmu_config_count() { + return GetHypercallInterface()->GetFuzzedUint32(); +} + +void kpc_set_config_arch() { + GetHypercallInterface()->Abort(); +} + +int kpc_set_period_arch(struct kpc_config_remote *mp_config) { + return 0; +} + +void kpc_set_running_arch() {} + +void kperf_debug_level() { + GetHypercallInterface()->Abort(); +} +void kperf_init() {} +void kperf_init_early() {} +void kperf_kdebug_active() { + GetHypercallInterface()->Abort(); +} +void kperf_kdebug_handler() { + GetHypercallInterface()->Abort(); +} +void kperf_kpc_thread_ast() { + GetHypercallInterface()->Abort(); +} +void kperf_lazy_cpu_action() { + GetHypercallInterface()->Abort(); +} +void kperf_lazy_make_runnable() {} +void kperf_lazy_off_cpu() {} +void kperf_on_cpu_active() { + GetHypercallInterface()->Abort(); +} +void kperf_on_cpu_internal() {} +void kperf_sample() { + GetHypercallInterface()->Abort(); +} +void kperf_status() { + GetHypercallInterface()->Abort(); +} + +void kptimer_running_setup() { + GetHypercallInterface()->Abort(); +} +void ktest_assertion_check() { + GetHypercallInterface()->Abort(); +} +// const char *T_SYM(current_file); +// const char *T_SYM(current_func); +// unsigned int T_SYM(current_line); +// void T_SYM(log)(const char *msg, ...) { +// GetHypercallInterface()->Abort(); +// } +// void T_SYM(set_current_expr)(const char *expr_fmt, ...) { +// GetHypercallInterface()->Abort(); +// } +// void T_SYM(set_current_msg)(const char *msg, ...) { +// GetHypercallInterface()->Abort(); +// } +// void T_SYM(set_current_var)(const char *name, const char *value_fmt, ...) { +// GetHypercallInterface()->Abort(); +// } +// T_SYM(temp) T_SYM(temp1), T_SYM(temp2), T_SYM(temp3); +// void ktest_testcase() { GetHypercallInterface()->Abort(); } +void kvtophys() { + GetHypercallInterface()->Abort(); +} + +void kdp_init() {} +void log_putc() { + GetHypercallInterface()->Abort(); +} +uint32_t low_eintstack[1]; +void lz4_decode_asm() { + GetHypercallInterface()->Abort(); +} +void mach_exception_raise() { + GetHypercallInterface()->Abort(); +} +void mach_exception_raise_state() { + GetHypercallInterface()->Abort(); +} +void mach_exception_raise_state_identity() { + GetHypercallInterface()->Abort(); +} +void machine_callstack() { + GetHypercallInterface()->Abort(); +} +processor_t machine_choose_processor(processor_set_t pset, + processor_t processor) { + GetHypercallInterface()->Abort(); +} +int machine_csv(cpuvn_e cve) { + return 0; +} +void machine_idle() { + GetHypercallInterface()->Abort(); +} +void machine_init() { + clock_config(); +} + +void machine_load_context(thread_t thread) { + GetHypercallInterface()->Abort(); +} + +void machine_lockdown() {} +void machine_max_runnable_latency() { + GetHypercallInterface()->Abort(); +} +void machine_signal_idle() { + GetHypercallInterface()->Abort(); +} +void machine_signal_idle_deferred() { + GetHypercallInterface()->Abort(); +} +vm_offset_t machine_stack_detach(thread_t thread) { + GetHypercallInterface()->Abort(); +} +void machine_stack_handoff(thread_t old, thread_t new) {} +void machine_switch_perfcontrol_context() {} +void machine_switch_perfcontrol_state_update() {} +kern_return_t machine_task_get_state(task_t task, int flavor, + thread_state_t state, + mach_msg_type_number_t *state_count) { + return KERN_SUCCESS; +} +void machine_task_init(task_t new_task, task_t parent_task, + boolean_t memory_inherit) {} +kern_return_t machine_task_set_state(task_t task, int flavor, + thread_state_t state, + mach_msg_type_number_t state_count) { + return KERN_SUCCESS; +} +void machine_task_terminate(task_t task) {} +void machine_tecs(thread_t thr) { + GetHypercallInterface()->Abort(); +} +boolean_t machine_timeout_suspended() { + GetHypercallInterface()->Abort(); +} +void machine_trace_thread() { + GetHypercallInterface()->Abort(); +} +void machine_trace_thread64() { + GetHypercallInterface()->Abort(); +} +void machine_track_platform_idle() { + GetHypercallInterface()->Abort(); +} +void machine_work_interval_notify() { + GetHypercallInterface()->Abort(); +} +uint64_t max_mem = 0; +void memset_word() { + GetHypercallInterface()->Abort(); +} +void ml_affinity_to_pset() { + GetHypercallInterface()->Abort(); +} +boolean_t ml_at_interrupt_context() { + GetHypercallInterface()->Abort(); +} +void ml_cpu_begin_state_transition(int cpu_id) { + GetHypercallInterface()->Abort(); +} +void ml_cpu_end_state_transition(int cpu_id) { + GetHypercallInterface()->Abort(); +} + +void ml_cpu_get_info(ml_cpu_info_t *info) { + info->cache_line_size = 64; +} + +void ml_delay_on_yield() { + GetHypercallInterface()->Abort(); +} +uint64_t ml_energy_stat(thread_t thread) { + GetHypercallInterface()->Abort(); +} +uint64_t ml_get_booter_memory_size(void) { + return 0; +} +boolean_t ml_get_interrupts_enabled() { + return GetHypercallInterface()->GetInterruptsEnabled(); +} +int ml_get_max_affinity_sets() { + return 0; +} +void ml_get_power_state(boolean_t *a, boolean_t *b) { + *a = GetHypercallInterface()->GetFuzzedBool(); + *b = GetHypercallInterface()->GetFuzzedBool(); +} +uint64_t ml_get_timebase(void) { + return 0; +} + +size_t ml_get_vm_reserved_regions(bool vm_is64bit, + struct vm_reserved_region **regions) { + *regions = NULL; + return 0; +} + +uint64_t ml_gpu_stat(thread_t thread) { + return 0; +} +void ml_gpu_stat_update(uint64_t gpu_ns_delta) { + GetHypercallInterface()->Abort(); +} + +vm_size_t ml_nofault_copy(vm_offset_t virtsrc, vm_offset_t virtdst, + vm_size_t size) { + GetHypercallInterface()->Abort(); +} + +void ml_panic_trap_to_debugger(const char *panic_format_str, + va_list *panic_args, unsigned int reason, + void *ctx, uint64_t panic_options_mask, + unsigned long panic_caller) { + GetHypercallInterface()->LogVaList(panic_format_str, *panic_args); + GetHypercallInterface()->Abort(); +} + +boolean_t ml_set_interrupts_enabled(boolean_t enable) { + // Yield before disabling + if (!enable) { + GetHypercallInterface()->Yield(); + } + bool result = GetHypercallInterface()->SetInterruptsEnabled(enable); + // Yield after reenabling + if (enable) { + GetHypercallInterface()->Yield(); + } + return result; +} + +vm_offset_t ml_static_ptovirt(vm_offset_t offset) { + GetHypercallInterface()->Abort(); +} + +boolean_t ml_validate_nofault(vm_offset_t virtsrc, vm_size_t size) { + GetHypercallInterface()->Abort(); +} +uint32_t ml_wait_max_cpus() { + return 4; +} +void mp_interrupt_watchdog() { + GetHypercallInterface()->Abort(); +} +void mp_kdp_enter(boolean_t proceed_on_failure) { + GetHypercallInterface()->Abort(); +} +void mp_kdp_exit() { + GetHypercallInterface()->Abort(); +} +void mp_rendezvous_lock() { + GetHypercallInterface()->Abort(); +} +void mp_rendezvous_unlock() { + GetHypercallInterface()->Abort(); +} +void msgbufp() { + GetHypercallInterface()->Abort(); +} +void nanoseconds_to_absolutetime(uint64_t nanoseconds, uint64_t *result) { + *result = nanoseconds; +} +void nanotime_to_absolutetime(clock_sec_t secs, clock_nsec_t nanosecs, + uint64_t *result) { + *result = ((uint64_t)secs * NSEC_PER_SEC) + nanosecs; +} +void no_shared_cr3() { + GetHypercallInterface()->Abort(); +} + +void pal_hib_rebuild_pmap_structs() { + GetHypercallInterface()->Abort(); +} +void pal_hib_teardown_pmap_structs() { + GetHypercallInterface()->Abort(); +} +bool panic_include_zprint = false; +void panic_include_ztrace() { + GetHypercallInterface()->Abort(); +} +mach_memory_info_t *panic_kext_memory_info; +vm_size_t panic_kext_memory_size; +void panic_print_symbol_name(vm_address_t search) { + GetHypercallInterface()->Abort(); +} +uintptr_t panic_stackshot_buf = 0; +size_t panic_stackshot_buf_len = 0; +void paniclog_flush() { + GetHypercallInterface()->Abort(); +} +void pcid_for_pmap_cpu_tuple() { + GetHypercallInterface()->Abort(); +} +struct percpu_base percpu_base = {.start = 0, .end = 0, .size = 0}; + +void physmap_base() { + GetHypercallInterface()->Abort(); +} +void physmap_max() { + GetHypercallInterface()->Abort(); +} +void plctrace_disable() { + GetHypercallInterface()->Abort(); +} +void plctrace_enabled() { + GetHypercallInterface()->Abort(); +} +void pmCPUHalt(uint32_t reason) { + GetHypercallInterface()->Abort(); +} +void pmSafeMode(x86_lcpu_t *lcpu, uint32_t flags) { + GetHypercallInterface()->Abort(); +} +bool pmap_supported_feature(pmap_t pmap, pmap_feature_flags_t feat) { + return true; +} +void mapping_set_mod(ppnum_t pn) { + GetHypercallInterface()->Abort(); +} +void pmap_adjust_unnest_parameters() { + GetHypercallInterface()->Abort(); +} +void pmap_advise_pagezero_range() {} +void pmap_asserts_enabled() { + GetHypercallInterface()->Abort(); +} +void pmap_asserts_traced() { + GetHypercallInterface()->Abort(); +} +void pmap_cache_attributes() { + GetHypercallInterface()->Abort(); +} +void pmap_change_wiring() {} +void pmap_clear_modify() {} +void pmap_clear_reference() {} +void pmap_clear_refmod() {} +void pmap_clear_refmod_options() {} +void pmap_clear_refmod_range_options() {} + +uint64_t pmap_commpage_size_min(pmap_t pmap) { + return 0x1000; +} + +void pmap_copy_page() {} +void pmap_copy_part_page() {} +void pmap_cs_allow_invalid() { + GetHypercallInterface()->Abort(); +} +void pmap_disable_NX() {} +void pmap_disconnect() {} +void pmap_disconnect_options() {} +void pmap_dump_page_tables() { + GetHypercallInterface()->Abort(); +} +kern_return_t pmap_enter() { + return 0; +} +void pmap_flush() {} +void pmap_flush_context_init() {} +bool pmap_get_jit_entitled() { + return GetHypercallInterface()->GetFuzzedBool(); +} +void pmap_get_refmod() { + GetHypercallInterface()->Abort(); +} +bool pmap_get_vm_map_cs_enforced() { + return GetHypercallInterface()->GetFuzzedBool(); +} +bool pmap_has_prot_policy() { + return false; +} +void pmap_hi_pages_done() {} +bool pmap_is_modified() { + return GetHypercallInterface()->GetFuzzedBool(); +} +bool pmap_is_referenced() { + return GetHypercallInterface()->GetFuzzedBool(); +} +void pmap_ledger_alloc() { + GetHypercallInterface()->Abort(); +} +void pmap_ledger_alloc_init() { + GetHypercallInterface()->Abort(); +} +void pmap_ledger_free() { + GetHypercallInterface()->Abort(); +} +void pmap_list_resident_pages() {} +void pmap_lock_phys_page() {} +kern_return_t pmap_map_block_addr() { + return KERN_SUCCESS; +} +void pmap_map_compressor_page() { + GetHypercallInterface()->Abort(); +} +kern_return_t pmap_nest() { + return KERN_SUCCESS; +} +void pmap_nesting_size_max() { + GetHypercallInterface()->Abort(); +} +void pmap_page_protect() { + GetHypercallInterface()->Abort(); +} +void pmap_page_protect_options() {} +void pmap_pageable() {} +void pmap_pcid_activate() { + GetHypercallInterface()->Abort(); +} +void pmap_pcid_ncpus() { + GetHypercallInterface()->Abort(); +} +void pmap_pre_expand() {} +void pmap_protect_options() {} +kern_return_t pmap_query_page_info() { + return KERN_FAILURE; +} +void pmap_query_resident() { + GetHypercallInterface()->Abort(); +} +void pmap_remove() {} +void pmap_require() {} +void pmap_set_cache_attributes() {} +void pmap_set_jit_entitled() {} +void pmap_set_noencrypt() { + GetHypercallInterface()->Abort(); +} +void pmap_set_process() {} +void pmap_set_vm_map_cs_enforced() {} +void pmap_shared_region_size_min() { + GetHypercallInterface()->Abort(); +} +void pmap_sync_page_attributes_phys() { + GetHypercallInterface()->Abort(); +} +void pmap_sync_page_data_phys() { + GetHypercallInterface()->Abort(); +} +void pmap_test_text_corruption() { + GetHypercallInterface()->Abort(); +} +void pmap_trim() { + GetHypercallInterface()->Abort(); +} +void pmap_unlock_phys_page() {} +void pmap_unmap_compressor_page() { + GetHypercallInterface()->Abort(); +} +void pmap_unnest() { + GetHypercallInterface()->Abort(); +} + +kern_return_t pmap_unnest_options() { + return KERN_SUCCESS; +} + +unsigned int random_bool_gen_bits(struct bool_gen *bg, unsigned int *buffer, + unsigned int count, unsigned int numbits) { + GetHypercallInterface()->Abort(); +} +void random_bool_init(struct bool_gen *bg) {} +unsigned int real_ncpus = 1; +uint64_t sane_size = 256 * MB; +void secure_memset() { + GetHypercallInterface()->Abort(); +} +void send_cpu_usage_violation() { + GetHypercallInterface()->Abort(); +} +void send_cpu_wakes_violation() { + GetHypercallInterface()->Abort(); +} +void send_disk_writes_violation() { + GetHypercallInterface()->Abort(); +} +void send_ktrace_background_available() { + GetHypercallInterface()->Abort(); +} +void send_sysdiagnose_notification_with_audit_token() { + GetHypercallInterface()->Abort(); +} +void serial_console_enabled() { + GetHypercallInterface()->Abort(); +} +void serial_getc() { + GetHypercallInterface()->Abort(); +} +void serial_putc() { + GetHypercallInterface()->Abort(); +} +void slave_machine_init(void *machine_param) { + GetHypercallInterface()->Abort(); +} +cpu_type_t slot_type(int slot_num) { + return 0; +} + +cpu_subtype_t slot_subtype(int slot_num) { + return 0; +} +cpu_subtype_t cpu_subtype(void) { + GetHypercallInterface()->Abort(); +} + +xstate_t current_xstate() { + return 0; +} +uint32_t get_eflags_exportmask() { + GetHypercallInterface()->Abort(); +} +void ml_fp_setvalid(boolean_t valid) {} +void write_random() { + GetHypercallInterface()->Abort(); +} + +void cpu_signal_handler(void) { + GetHypercallInterface()->Abort(); +} +void handle_pending_TLB_flushes(void) { + GetHypercallInterface()->Abort(); +} +void NMIPI_panic(cpumask_t cpus, NMI_reason_t reason) { + GetHypercallInterface()->Abort(); +} +void hw_lock_byte_lock(volatile uint8_t *lock_byte) { + *lock_byte = 1; +} +void hw_lock_byte_unlock(volatile uint8_t *lock_byte) { + *lock_byte = 0; +} + +cpu_threadtype_t slot_threadtype(int slot_num) { + return 0; +} +void telemetry_notification() { + GetHypercallInterface()->Abort(); +} +void thread_tell_urgency(thread_urgency_t urgency, uint64_t rt_period, + uint64_t rt_deadline, uint64_t sched_latency, + thread_t nthread) {} +void top_ztrace() { + GetHypercallInterface()->Abort(); +} +void trace_backtrace() { + GetHypercallInterface()->Abort(); +} +void track_this_zone() { + GetHypercallInterface()->Abort(); +} +void trust_cache_init() {} +void uext_server() { + GetHypercallInterface()->Abort(); +} +void version() { + GetHypercallInterface()->Abort(); +} +void version_major() { + GetHypercallInterface()->Abort(); +} +void version_minor() { + GetHypercallInterface()->Abort(); +} +vm_offset_t vm_hib_base = 0; + +vm_offset_t vm_kernel_builtinkmod_text = 0; +vm_offset_t vm_kernel_builtinkmod_text_end = 0; +vm_offset_t vm_kernel_slid_base = 0; +vm_offset_t vm_kernel_slid_top = 0; +vm_offset_t vm_kernel_slide = 0; +vm_offset_t vm_kernel_stext = 0; +vm_offset_t vm_kernel_top = 0; + +boolean_t vmx_hv_support() { + return false; +} +void xts_decrypt() { + GetHypercallInterface()->Abort(); +} +void xts_encrypt() { + GetHypercallInterface()->Abort(); +} +void xts_start() { + GetHypercallInterface()->Abort(); +} +uLong zlib_deflate_memory_size(int wbits, int memlevel) { + GetHypercallInterface()->Abort(); +} + +// TODO(nedwill): try increasing this to simulate multi-core +// and make additional calls to __smr_init +unsigned zpercpu_count(void) { + return 1; +} + +void default_pager_memory_object_create() { + GetHypercallInterface()->Abort(); +} +void do_mach_notify_dead_name() { + GetHypercallInterface()->Abort(); +} +void do_mach_notify_no_senders() { + GetHypercallInterface()->Abort(); +} +void do_mach_notify_port_deleted() { + GetHypercallInterface()->Abort(); +} +void do_mach_notify_port_destroyed() { + GetHypercallInterface()->Abort(); +} +void do_mach_notify_send_once() { + GetHypercallInterface()->Abort(); +} +kern_return_t host_get_UNDServer() { + return KERN_FAILURE; +} +kern_return_t host_set_UNDServer() { + return KERN_FAILURE; +} +kern_return_t kext_request() { + return KERN_FAILURE; +} +kern_return_t mach_memory_info() { + return KERN_FAILURE; +} +void mach_vm_region_info() { + GetHypercallInterface()->Abort(); +} +void mach_vm_region_info_64() { + GetHypercallInterface()->Abort(); +} +void mach_zone_force_gc() {} +kern_return_t mach_zone_get_btlog_records() { + return KERN_FAILURE; +} +void mach_zone_get_zlog_zones() {} +kern_return_t mach_zone_info() { + return KERN_FAILURE; +} +kern_return_t mach_zone_info_for_largest_zone() { + return KERN_FAILURE; +} +kern_return_t mach_zone_info_for_zone() { + return KERN_FAILURE; +} +kern_return_t task_zone_info() { + return KERN_FAILURE; +} +void vm_mapped_pages_info() { + GetHypercallInterface()->Abort(); +} + +void commpage_update_atm_diagnostic_config(uint32_t diagnostic_config) {} + +void cpu_exit_wait() { + GetHypercallInterface()->Abort(); +} +void cpu_sleep() { + GetHypercallInterface()->Abort(); +} +void hibernate_vm_lock() { + GetHypercallInterface()->Abort(); +} +void hibernate_vm_unlock() { + GetHypercallInterface()->Abort(); +} +void init_ast_check() {} + +bool ml_cpu_can_exit(int cpu_id, processor_reason_t reason) { + GetHypercallInterface()->Abort(); +} + +void ml_cpu_down() { + GetHypercallInterface()->Abort(); +} +void ml_cpu_up() { + GetHypercallInterface()->Abort(); +} +void panic_notify() { + GetHypercallInterface()->Abort(); +} +void phyreadpanic() { + GetHypercallInterface()->Abort(); +} +void phywritepanic() { + GetHypercallInterface()->Abort(); +} +uintptr_t pmap_verify_noncacheable(uintptr_t vaddr) { + GetHypercallInterface()->Abort(); +} +void reportphyreaddelayabs() { + GetHypercallInterface()->Abort(); +} +void reportphywritedelayabs() { + GetHypercallInterface()->Abort(); +} + +kern_return_t machine_thread_get_state(thread_t thread, thread_flavor_t flavor, + thread_state_t state, + mach_msg_type_number_t *count) { + GetHypercallInterface()->GetFuzzedBytes(state, *count * sizeof(*state)); + return KERN_SUCCESS; +} +void machine_thread_going_off_core() {} +void machine_thread_going_on_core() {} +kern_return_t machine_thread_inherit_taskwide(thread_t thread, + task_t parent_task) { + return KERN_SUCCESS; +} +mach_vm_address_t machine_thread_pc(thread_t thread) { + GetHypercallInterface()->Abort(); +} +void machine_thread_reset_pc(thread_t thread, mach_vm_address_t pc) { + GetHypercallInterface()->Abort(); +} + +extern void machine_thread_set_insn_copy_optout(thread_t thr) {} + +kern_return_t machine_thread_set_state(thread_t thread, thread_flavor_t flavor, + thread_state_t state, + mach_msg_type_number_t count) { + return KERN_SUCCESS; +} +kern_return_t machine_thread_set_tsd_base(thread_t thread, + mach_vm_offset_t tsd_base) { + GetHypercallInterface()->Abort(); +} +kern_return_t machine_thread_state_convert_to_user( + thread_t thread, thread_flavor_t flavor, thread_state_t tstate, + mach_msg_type_number_t *count, thread_set_status_flags_t tssf_flags) { + return KERN_SUCCESS; +} +void machine_thread_state_initialize(thread_t thread) { + GetHypercallInterface()->Abort(); +} +void machine_thread_switch_addrmode(thread_t thread) { + // machine_thread_create(thread, task, false); +} +void machine_thread_template_init(thread_t thr_template) {} +bool IOCurrentTaskHasEntitlement() { + return true; +} +void IOPMRootDomainGetWillShutdown() { + GetHypercallInterface()->Abort(); +} +void IOSetImageBoot() { + GetHypercallInterface()->Abort(); +} +void OSKextGetRefGrpForCaller() { + GetHypercallInterface()->Abort(); +} + +void _storage_lock() { + GetHypercallInterface()->Abort(); +} +void _storage_unlock() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_key_gcm() { + GetHypercallInterface()->Abort(); +} +void aes_encrypt_set_iv_gcm() { + GetHypercallInterface()->Abort(); +} + +const char *core_analytics_ca_bool_cpp_stringified = "TODO"; + +void core_analytics_send_event_lazy() { + // TODO(nedwill): implement coreanalytics +} + +void *core_analytics_family_match() { + return NULL; +} + +void create_buffers() { + GetHypercallInterface()->Abort(); +} +void create_buffers_triage() {} +void delete_buffers() { + GetHypercallInterface()->Abort(); +} +void disable_wrap() { + GetHypercallInterface()->Abort(); +} +void host_security_create_task_token() { + GetHypercallInterface()->Abort(); +} +void host_security_set_task_token() { + GetHypercallInterface()->Abort(); +} +void hvg_hcall_set_coredump_data() { + GetHypercallInterface()->Abort(); +} +void is_iokit_subsystem() { + GetHypercallInterface()->Abort(); +} +void kdbg_cpu_count() { + GetHypercallInterface()->Abort(); +} +void kdbg_debug() { + GetHypercallInterface()->Abort(); +} +void kdebug_lck_grp() { + GetHypercallInterface()->Abort(); +} +void kdebug_lck_init() {} +uint64_t kdebug_timestamp() { + GetHypercallInterface()->Abort(); +} +vm_map_t kernel_data_map_get() { + GetHypercallInterface()->Abort(); +} +void kernel_debug_read() { + GetHypercallInterface()->Abort(); +} +void kernel_debug_write() { + GetHypercallInterface()->Abort(); +} +void kernel_triage_extract(uint64_t thread_id, void *buf, uint32_t bufsz) { + GetHypercallInterface()->Abort(); +} +void kernel_triage_record(uint64_t thread_id, uint64_t debugid, + uintptr_t arg1) {} +lck_spinlock_to_info_t lck_spinlock_timeout_hit(void *lck, uintptr_t owner) { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_dropped_nomem() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_dropped_off() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_mem_allocated() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_mem_failed() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_mem_released() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_queued() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_received() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_rejected_fh() { + GetHypercallInterface()->Abort(); +} +void log_queue_cnt_sent() { + GetHypercallInterface()->Abort(); +} +void mach_exception_raise_identity_protected() { + GetHypercallInterface()->Abort(); +} +void mach_test_sync_upcall() {} +void ml_cpu_get_info_type(ml_cpu_info_t *ml_cpu_info, + cluster_type_t cluster_type) {} +int ml_early_cpu_max_number() { + return 1; +} +unsigned int ml_get_cpu_number_type(cluster_type_t cluster_type __unused, + bool logical, bool available) { + GetHypercallInterface()->Abort(); +} +unsigned int ml_get_cpu_types(void) { + return GetHypercallInterface()->GetFuzzedUint32InRange(0, 0xFFFFFFFFU); +} +void pmap_cs_fork_prepare() {} +bool pmap_in_ppl() { + return false; +} +void pmap_ledger_verify_size() { + GetHypercallInterface()->Abort(); +} +void ptrauth_utils_auth_blob_generic() { + GetHypercallInterface()->Abort(); +} +void ptrauth_utils_sign_blob_generic() { + GetHypercallInterface()->Abort(); +} +void read_lbr() { + GetHypercallInterface()->Abort(); +} +uint64_t report_phy_read_delay = 0; +uint32_t report_phy_read_osbt = 0; +uint64_t report_phy_write_delay = 0; +uint32_t report_phy_write_osbt = 0; +void send_vfs_resolve_dir() { + GetHypercallInterface()->Abort(); +} +void send_vfs_resolve_file() { + GetHypercallInterface()->Abort(); +} +void serial_putc_options() { + GetHypercallInterface()->Abort(); +} +uint64_t trace_phy_read_delay = 0; +uint64_t trace_phy_write_delay = 0; +void uuid_unparse_lower(const uuid_t uu, uuid_string_t out) { + GetHypercallInterface()->Abort(); +} + +__abortlike void zone_invalid_panic(zone_t zone) { + panic("zone %p isn't in the zone_array", zone); +} + +void zone_ro_elem_size() { + GetHypercallInterface()->Abort(); +} +void zone_userspace_reboot_checks(void) { + GetHypercallInterface()->Abort(); +} + +void workq_schedule_delayed_thread_creation() { + GetHypercallInterface()->Abort(); +} +void bsdthread_get_max_parallelism() { + GetHypercallInterface()->Abort(); +} +void _wq_thactive_move() { + GetHypercallInterface()->Abort(); +} +void _wq_thactive_refresh_best_constrained_req_qos() { + GetHypercallInterface()->Abort(); +} +void workq_lock_try() { + GetHypercallInterface()->Abort(); +} +void _wq_thactive() { + GetHypercallInterface()->Abort(); +} +void workq_lock_is_acquired_kdp() { + GetHypercallInterface()->Abort(); +} +void _wq_thactive_inc() { + GetHypercallInterface()->Abort(); +} +void workq_unpark_continue() { + GetHypercallInterface()->Abort(); +} +void _wq_thactive_dec() { + GetHypercallInterface()->Abort(); +} +void __atomic_fetch_add_16() { + GetHypercallInterface()->Abort(); +} +void __atomic_fetch_sub_16() { + GetHypercallInterface()->Abort(); +} +bool kdp_lck_ticket_is_acquired(lck_ticket_t *lck) { + GetHypercallInterface()->Abort(); +} + +kern_return_t machine_thread_siguctx_pointer_convert_to_user( + __unused thread_t thread, __unused user_addr_t *uctxp) { + // No conversion to userspace representation on this platform + return KERN_SUCCESS; +} + +vm_offset_t panic_fault_address = 0; + +int hv_support_available = 0; + +int hv_disable = 0; + +void kdebug_storage_lock() { + GetHypercallInterface()->Abort(); +} +void kdebug_storage_unlock() { + GetHypercallInterface()->Abort(); +} +void mac_audit_data_zone() { + GetHypercallInterface()->Abort(); +} +void IOSKArenaCreate() { + GetHypercallInterface()->Abort(); +} +void IOSKArenaDestroy() { + GetHypercallInterface()->Abort(); +} +void IOSKMapperCreate() { + GetHypercallInterface()->Abort(); +} +void IOSKMapperGetAddress() { + GetHypercallInterface()->Abort(); +} +void IOSKMapperDestroy() { + GetHypercallInterface()->Abort(); +} +void IOSKMapperRedirect() { + GetHypercallInterface()->Abort(); +} +int flsll(unsigned long long mask) { + if (mask == 0) { + return 0; + } + + return (sizeof(mask) << 3) - __builtin_clzll(mask); +} +void *IOSKRegionCreate() { + return (void *)1; +} +void IOSKRegionDestroy() { + GetHypercallInterface()->Abort(); +} + +void *IOSKMemoryBufferCreate() { + return (void *)1; +} + +int IOSKMemoryReclaim() { + return 0; +} + +int IOSKMemoryWire() { + return 0; +} + +int IOSKRegionSetBuffer() { + return 0; +} + +bool IOSKBufferIsWired() { + return true; +} + +void IOSKRegionClearBufferDebug() { + GetHypercallInterface()->Abort(); +} +void IOSKMemoryUnwire() { + GetHypercallInterface()->Abort(); +} +void IOSKMemoryDiscard() { + GetHypercallInterface()->Abort(); +} +void IOSKMemoryDestroy() { + GetHypercallInterface()->Abort(); +} +void os_memcmp_mask_64B() { + GetHypercallInterface()->Abort(); +} +void os_memcmp_mask_80B() { + GetHypercallInterface()->Abort(); +} +void os_memcmp_mask_48B() { + GetHypercallInterface()->Abort(); +} +void os_cpu_copy_in_cksum() { + GetHypercallInterface()->Abort(); +} +void OSKernelStackRemaining() { + GetHypercallInterface()->Abort(); +} +void hv_support_init() {} + +uint32_t hw_wait_while_equals32(uint32_t *address, uint32_t current) { + GetHypercallInterface()->Abort(); +} diff --git a/fuzz/xnu/osfmk/fakes/platform_expert.c b/fuzz/xnu/osfmk/fakes/platform_expert.c new file mode 100644 index 0000000..6844a22 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/platform_expert.c @@ -0,0 +1,112 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void PEGetCoprocessorVersion() { + GetHypercallInterface()->Abort(); +} + +void PEHaltRestartInternal() { + GetHypercallInterface()->Abort(); +} + +void PEInitiatePanic() { + GetHypercallInterface()->Abort(); +} + +char *PE_boot_args() { + return "boot_args"; +} + +void PE_display_icon(unsigned int flags, const char *name) {} + +void PEHaltRestart() {} + +boolean_t PE_get_default(const char *property_name, void *property_ptr, + unsigned int max_property) { + // TODO(nedwill): fuzz this + memset(property_ptr, 0, max_property); + return false; + // return true; +} + +void *PE_get_kc_header(kc_kind_t type) { + GetHypercallInterface()->Abort(); +} + +bool PE_get_primary_kc_format(kc_format_t *type) { + *type = KCFormatStatic; + return true; +} + +uint32_t PE_i_can_has_debugger(uint32_t *debug_flags) { + if (debug_flags) { + *debug_flags = 1; + } + return 1; +} + +void PE_init_iokit() {} + +void PE_init_panicheader() { + GetHypercallInterface()->Abort(); +} + +void (*PE_kputc)(char c) = NULL; + +void PE_lockdown_iokit() {} + +void PE_panic_hook(const char *str) { + GetHypercallInterface()->Abort(); +} + +boolean_t PE_parse_boot_arg_str(const char *arg_string, char *arg_ptr, + int size) { + bzero(arg_ptr, size); + return true; +} + +static boot_args args; + +PE_state_t PE_state = {.bootArgs = &args}; + +int PE_stub_poll_input(unsigned int options, char *c) { + GetHypercallInterface()->Abort(); +} + +void PE_update_panicheader_nestedpanic() { + GetHypercallInterface()->Abort(); +} diff --git a/fuzz/xnu/osfmk/fakes/pmap.c b/fuzz/xnu/osfmk/fakes/pmap.c new file mode 100644 index 0000000..c2a6947 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/pmap.c @@ -0,0 +1,167 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include + +struct pmap kernel_pmap_store; +const pmap_t kernel_pmap = &kernel_pmap_store; + +void pmap_remove_options(pmap_t pmap, vm_map_address_t start, + vm_map_address_t end, int options) {} + +void pmap_init() {} + +void pmap_assert_free() {} + +boolean_t pmap_is_noencrypt(ppnum_t page) { + return false; +} + +bool pmap_verify_free(ppnum_t pn) { + return true; +} + +kern_return_t pmap_enter_options(pmap_t pmap, vm_map_offset_t v, ppnum_t pn, + vm_prot_t prot, vm_prot_t fault_type, + unsigned int flags, boolean_t wired, + unsigned int options, void *arg, + pmap_mapping_type_t mapping_type) { + return KERN_SUCCESS; +} + +kern_return_t pmap_enter_options_addr(pmap_t pmap, vm_map_offset_t v, + pmap_paddr_t pa, vm_prot_t prot, + vm_prot_t fault_type, unsigned int flags, + boolean_t wired, unsigned int options, + void *arg, + pmap_mapping_type_t mapping_type) { + return KERN_SUCCESS; +} + +boolean_t pmap_is_empty(pmap_t pmap, vm_map_offset_t start, + vm_map_offset_t end) { + return true; +} + +void pmap_reference(pmap_t pmap) {} + +void mapping_free_prime() {} + +// TODO(nedwill); reset pmap? +static int next_page = 0; + +void *calloc(size_t nmemb, size_t size); +void free(void *ptr); + +pmap_t pmap_create_options(/* Create a pmap_t. */ + ledger_t ledger, vm_map_size_t size, + unsigned int flags) { + return calloc(1, sizeof(struct pmap)); +} + +void pmap_destroy(pmap_t pmap) { + free(pmap); +} + +// 256 MiB +const ppnum_t NUM_PAGES = 65536; + +boolean_t pmap_next_page(ppnum_t *pn) { + *pn = next_page++; + // TODO(nedwill): fuzz pmap + return *pn <= NUM_PAGES; +} + +kern_return_t pmap_next_page_large(ppnum_t *pn) { + *pn = next_page++; + // TODO(nedwill): fuzz pmap + return *pn <= NUM_PAGES; +} + +boolean_t pmap_next_page_hi(ppnum_t *pnum, boolean_t might_free) { + *pnum = next_page++; + return true; +} +boolean_t pmap_valid_page(ppnum_t pn) { + // TODO(nedwill): fuzz + return true; +} + +static vm_offset_t start_pmap; + +int posix_memalign(void **memptr, size_t alignment, size_t size); + +void pmap_virtual_space(vm_offset_t *virtual_start, vm_offset_t *virtual_end) { + void *ptr; + int ret = posix_memalign(&ptr, 0x1000, 0x1000 * NUM_PAGES); + if (ret) { + GetHypercallInterface()->Abort(); + } + start_pmap = (vm_offset_t)ptr; + *virtual_start = start_pmap; + *virtual_end = start_pmap + (0x1000 * NUM_PAGES); +} + +uint_t pmap_free_pages() { + return NUM_PAGES; +} + +void pmap_zero_page(ppnum_t pn) {} +void(pmap_zero_part_page)(ppnum_t pn, vm_offset_t offset, vm_size_t len) {} + +kern_return_t pmap_pre_expand_large(pmap_t pmap, vm_map_offset_t vaddr) { + return KERN_SUCCESS; +} + +void mapping_adjust() {} + +ppnum_t IOMapperIOVMAlloc(unsigned pages) { + return 0; +} + +void ml_static_mfree(vm_offset_t addr, vm_size_t size) {} + +vm_size_t pmap_query_pagesize(pmap_t map, vm_map_offset_t vaddr) { + return I386_PGBYTES; +} + +void pmap_protect(pmap_t pmap, vm_map_address_t s, vm_map_address_t e, + vm_prot_t prot) {} + +uint64_t pmap_release_pages_fast() { + return 0; +} +void pmap_clear_noencrypt(ppnum_t pn) {} + +// TODO(nedwill): actually simulate physical page mappings +ppnum_t pmap_find_phys(pmap_t map, addr64_t va) { + return 0; +} diff --git a/fuzz/xnu/osfmk/fakes/scheduler.c b/fuzz/xnu/osfmk/fakes/scheduler.c new file mode 100644 index 0000000..159333e --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/scheduler.c @@ -0,0 +1,307 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +void init(void) { + current_processor()->processor_set = &pset0; +} + +void timebase_init(void) { + GetHypercallInterface()->Abort(); +} + +void maintenance_continuation(void) { + assert_wait((event_t)0x41414141, TH_UNINT); + thread_block((void *)maintenance_continuation); +} + +thread_t choose_thread(processor_t processor, int priority, ast_t reason) { + GetHypercallInterface()->Abort(); +} + +/* True if scheduler supports stealing threads for this pset */ +bool steal_thread_enabled(processor_set_t pset) { + return false; +} + +/* + * Steal a thread from another processor in the pset so that it can run + * immediately + */ +thread_t steal_thread(processor_set_t pset) { + GetHypercallInterface()->Abort(); +} + +/* + * Compute priority for a timeshare thread based on base priority. + */ +int compute_timeshare_priority(thread_t thread) { + GetHypercallInterface()->Abort(); +} + +/* + * Pick the best node for a thread to run on. + */ +pset_node_t choose_node(thread_t thread) { + return &pset_node0; +} + +processor_t choose_processor_stub(processor_set_t pset, processor_t processor, + thread_t thread) { + return master_processor; +} + +/* + * Enqueue a timeshare or fixed priority thread onto the per-processor + * runqueue + */ +boolean_t processor_enqueue(processor_t processor, thread_t thread, + sched_options_t options) { + GetHypercallInterface()->Abort(); +} + +/* Migrate threads away in preparation for processor shutdown */ +void processor_queue_shutdown(processor_t processor) { + GetHypercallInterface()->Abort(); +} + +/* Remove the specific thread from the per-processor runqueue */ +boolean_t processor_queue_remove(processor_t processor, thread_t thread) { + GetHypercallInterface()->Abort(); +} + +/* + * Does the per-processor runqueue have any timeshare or fixed priority + * threads on it? Called without pset lock held, so should + * not assume immutability while executing. + */ +boolean_t processor_queue_empty(processor_t processor) { + GetHypercallInterface()->Abort(); +} + +/* + * Does the per-processor runqueue contain runnable threads that + * should cause the currently-running thread to be preempted? + */ +ast_t processor_csw_check(processor_t processor) { + return AST_NONE; +} + +/* + * Does the per-processor runqueue contain a runnable thread + * of > or >= priority, as a preflight for choose_thread() or other + * thread selection + */ +boolean_t processor_queue_has_priority(processor_t processor, int priority, + boolean_t gte) { + GetHypercallInterface()->Abort(); +} + +/* Quantum size for the specified non-realtime thread. */ +uint32_t initial_quantum_size(thread_t thread) { + // TODO(nedwill): figure out a good value here + return 5; +} + +/* Scheduler mode for a new thread */ +sched_mode_t initial_thread_sched_mode(task_t parent_task) { + return TH_MODE_FIXED; +} + +/* Callback for non-realtime threads when the quantum timer fires */ +void quantum_expire(thread_t thread) { + GetHypercallInterface()->Abort(); +} + +/* + * Runnable threads on per-processor runqueue. Should only + * be used for relative comparisons of load between processors. + */ +int processor_runq_count(processor_t processor) { + GetHypercallInterface()->Abort(); +} + +/* Aggregate runcount statistics for per-processor runqueue */ +uint64_t processor_runq_stats_count_sum(processor_t processor) { + GetHypercallInterface()->Abort(); +} + +boolean_t processor_bound_count(processor_t processor) { + return true; +} + +void thread_update_scan(sched_update_scan_context_t scan_context) { + GetHypercallInterface()->Abort(); +} + +/* Returns true if this processor should avoid running this thread. */ +bool thread_avoid_processor(processor_t processor, thread_t thread) { + GetHypercallInterface()->Abort(); +} + +/* + * Invoked when a processor is about to choose the idle thread + * Used to send IPIs to a processor which would be preferred to be idle instead. + * Called with pset lock held, returns pset lock unlocked. + */ +void processor_balance(processor_t processor, processor_set_t pset) { + pset_unlock(pset); +} + +rt_queue_t rt_runq(processor_set_t pset) { + return &pset->rt_runq; +} + +void rt_init(processor_set_t pset) {} +void rt_queue_shutdown(processor_t processor) { + GetHypercallInterface()->Abort(); +} +void rt_runq_scan(sched_update_scan_context_t scan_context) { + GetHypercallInterface()->Abort(); +} +int64_t rt_runq_count_sum(void) { + GetHypercallInterface()->Abort(); +} +thread_t rt_steal_thread(processor_set_t pset, uint64_t earliest_deadline) { + GetHypercallInterface()->Abort(); +} + +void check_spill(processor_set_t pset, thread_t thread) {} +sched_ipi_type_t ipi_policy(processor_t dst, thread_t thread, + boolean_t dst_idle, sched_ipi_event_t event) { + GetHypercallInterface()->Abort(); +} + +bool thread_should_yield(processor_t processor, thread_t thread) { + return thread != main_thread; +} + +/* Routine to update run counts */ +uint32_t run_count_incr(thread_t thread) { + return 1; +} +uint32_t run_count_decr(thread_t thread) { + return 1; +} + +/* Routine to update scheduling bucket for a thread */ +void update_thread_bucket(thread_t thread) { + thread->th_sched_bucket = TH_BUCKET_RUN; + // TODO(nedwill): determine best value for this + thread->pri_shift = INT8_MAX; +} + +/* Routine to inform the scheduler when a new pset becomes schedulable */ +void pset_made_schedulable(processor_t processor, processor_set_t pset, + boolean_t drop_lock) { + GetHypercallInterface()->Abort(); +} +/* Routine to inform the scheduler when all CPUs have finished initializing */ +void cpu_init_completed(void) { + GetHypercallInterface()->Abort(); +} +/* Routine to check if a thread is eligible to execute on a specific pset */ +bool thread_eligible_for_pset(thread_t thread, processor_set_t pset) { + GetHypercallInterface()->Abort(); +} + +void pset_init_stub(processor_set_t pset) {} + +void processor_init_stub(processor_t p) {} + +uint32_t qos_max_parallelism_nop(int qos, uint64_t options) { + return 0; +} + +const struct sched_dispatch_table sched_dualq_dispatch = { + .sched_name = "SockScheduler", + .init = init, + .timebase_init = timebase_init, + .processor_init = processor_init_stub, + .pset_init = pset_init_stub, + .maintenance_continuation = maintenance_continuation, + .choose_thread = choose_thread, + .steal_thread_enabled = steal_thread_enabled, + .steal_thread = steal_thread, + .compute_timeshare_priority = compute_timeshare_priority, + .choose_node = choose_node, + .choose_processor = choose_processor_stub, + .processor_enqueue = processor_enqueue, + .processor_queue_shutdown = processor_queue_shutdown, + .processor_queue_remove = processor_queue_remove, + .processor_queue_empty = processor_queue_empty, + .priority_is_urgent = priority_is_urgent, + .processor_csw_check = processor_csw_check, + .processor_queue_has_priority = processor_queue_has_priority, + .initial_quantum_size = initial_quantum_size, + .initial_thread_sched_mode = initial_thread_sched_mode, + .can_update_priority = can_update_priority, + .update_priority = update_priority, + .lightweight_update_priority = lightweight_update_priority, + .quantum_expire = quantum_expire, + .processor_runq_count = processor_runq_count, + .processor_runq_stats_count_sum = processor_runq_stats_count_sum, + .processor_bound_count = processor_bound_count, + .thread_update_scan = thread_update_scan, + .multiple_psets_enabled = false, + .sched_groups_enabled = false, + .avoid_processor_enabled = false, + .thread_avoid_processor = thread_avoid_processor, + .processor_balance = processor_balance, + .rt_runq = rt_runq, + .rt_init = rt_init, + .rt_queue_shutdown = rt_queue_shutdown, + .rt_runq_scan = rt_runq_scan, + .rt_runq_count_sum = rt_runq_count_sum, + .rt_steal_thread = rt_steal_thread, + .qos_max_parallelism = qos_max_parallelism_nop, + .check_spill = check_spill, + .ipi_policy = ipi_policy, + .thread_should_yield = thread_should_yield, + .run_count_incr = run_count_incr, + .run_count_decr = run_count_decr, + .update_thread_bucket = update_thread_bucket, + .pset_made_schedulable = pset_made_schedulable, + /* Routine to inform the scheduler when all CPUs have finished initializing + */ + .cpu_init_completed = cpu_init_completed, + /* Routine to check if a thread is eligible to execute on a specific pset */ + .thread_eligible_for_pset = thread_eligible_for_pset, +}; diff --git a/fuzz/xnu/osfmk/fakes/sync/attr.cc b/fuzz/xnu/osfmk/fakes/sync/attr.cc new file mode 100644 index 0000000..50f0839 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/attr.cc @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + + +extern "C" { +void *lck_attr_alloc_init() { + return (void *)1; +} +void lck_attr_free() {} +void lck_attr_setdebug() {} +void lck_attr_setdefault() {} +void lck_attr_startup_init() {} +} diff --git a/fuzz/xnu/osfmk/fakes/sync/group.cc b/fuzz/xnu/osfmk/fakes/sync/group.cc new file mode 100644 index 0000000..9b46951 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/group.cc @@ -0,0 +1,51 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include + +extern "C" { +void *lck_grp_alloc_init() { + return (void *)1; +} +void *lck_grp_attr_alloc_init() { + return (void *)1; +} +void lck_grp_attr_free() {} +void lck_grp_attr_setdefault() {} +void lck_grp_free() {} +void lck_grp_startup_init() {} +void lck_grp_init() { + abort(); +} +void lck_grp_lckcnt_incr() { + abort(); +} +void lck_grp_reference() { + abort(); +} +} diff --git a/fuzz/xnu/osfmk/fakes/sync/locks.cc b/fuzz/xnu/osfmk/fakes/sync/locks.cc new file mode 100644 index 0000000..9f4f27e --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/locks.cc @@ -0,0 +1,241 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +struct lck_grp_t; + +// VirtualLckPtr class definition +class VirtualLckPtr { + public: + VirtualLckPtr(void* initial_value) : protected_value(initial_value) { + mutex = GetHypercallInterface()->AllocateMutex(); + } + + ~VirtualLckPtr() { + GetHypercallInterface()->FreeMutex(mutex); + } + + void* GetValue() { return protected_value; } + void SetValue(void* value) { protected_value = value; } + + void LockExclusive() { GetHypercallInterface()->MutexLock(mutex); } + void UnlockExclusive() { GetHypercallInterface()->MutexUnlock(mutex); } + + bool IsLocked() { return GetHypercallInterface()->MutexIsOwned(mutex); } + + // TODO(nedwill): this is quite wasteful to yield so often when we may not be "runnable" + // yet due to the value not matching. We should add a mechanism similar to abseil's mutex + // that lets you wait for a condition to be true and the outstanding conditions are evaluated + // every time the scheduler wants to wake up an eligible mutex (IIUC that's how it works). + void WaitForValue(void* value) { + while (GetValue() != value) { + UnlockExclusive(); + GetHypercallInterface()->Yield(); + LockExclusive(); + } + } + + private: + HyperMutex* mutex; + void* protected_value; +}; + +// Define the hw_lck_ptr_t structure +struct hw_lck_ptr_t { + VirtualLckPtr* virtual_lck_ptr; +}; + +extern "C" { + +// General Lock Handling Functions +void hw_lock_init(HyperMutex **sp) { + *sp = GetHypercallInterface()->AllocateMutex(); +} + +unsigned int hw_lock_try(HyperMutex **sp, lck_grp_t *grp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*sp); +} + +void hw_lock_unlock(HyperMutex **sp) { + GetHypercallInterface()->MutexUnlock(*sp); + GetHypercallInterface()->Yield(); +} + +void hw_lock_held(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->AssertMutexHeld(*sp); +} + +void hw_lock_to_init(HyperMutex **sp) { + *sp = GetHypercallInterface()->AllocateMutex(); +} + +int hw_lock_to(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*sp); +} + +// hw_lck_ptr_t Handling Functions +void hw_lck_ptr_init(hw_lck_ptr_t *lck, void *val, lck_grp_t *grp) { + lck->virtual_lck_ptr = new VirtualLckPtr(val); +} + +void hw_lck_ptr_destroy(hw_lck_ptr_t *lck, lck_grp_t *grp) { + delete lck->virtual_lck_ptr; + lck->virtual_lck_ptr = nullptr; +} + +void *hw_lck_ptr_value(hw_lck_ptr_t *lck) { + if (!lck->virtual_lck_ptr) { + GetHypercallInterface()->Abort(); + } + return lck->virtual_lck_ptr->GetValue(); +} + +void *hw_lck_ptr_lock(hw_lck_ptr_t *lck, lck_grp_t *grp) { + if (!lck->virtual_lck_ptr) { + GetHypercallInterface()->Abort(); + } + lck->virtual_lck_ptr->LockExclusive(); + return lck->virtual_lck_ptr->GetValue(); +} + +// Use hw_lck_ptr_lock for both lock and lock_nopreempt +void *hw_lck_ptr_lock_nopreempt(hw_lck_ptr_t *lck, lck_grp_t *grp) { + return hw_lck_ptr_lock(lck, grp); +} + +void hw_lck_ptr_unlock(hw_lck_ptr_t *lck, void *val, lck_grp_t *grp) { + if (!lck->virtual_lck_ptr) { + GetHypercallInterface()->Abort(); + } + lck->virtual_lck_ptr->SetValue(val); + lck->virtual_lck_ptr->UnlockExclusive(); +} + +// Use hw_lck_ptr_unlock for both unlock and unlock_nopreempt +void hw_lck_ptr_unlock_nopreempt(hw_lck_ptr_t *lck, void *val, lck_grp_t *grp) { + hw_lck_ptr_unlock(lck, val, grp); +} + +bool hw_lck_ptr_held(hw_lck_ptr_t *lck) { + if (!lck->virtual_lck_ptr) { + GetHypercallInterface()->Abort(); + } + return lck->virtual_lck_ptr->IsLocked(); +} + +void hw_lck_ptr_wait_for_value(hw_lck_ptr_t *lck, void *val, lck_grp_t *grp) { + if (!lck->virtual_lck_ptr) { + GetHypercallInterface()->Abort(); + } + lck->virtual_lck_ptr->WaitForValue(val); +} + +void hw_lck_ptr_wait_for_value_contended(hw_lck_ptr_t *lck, void *val, + lck_grp_t *grp) { + hw_lck_ptr_wait_for_value(lck, val, grp); +} + +void *hw_lck_ptr_lock_fastpath(hw_lck_ptr_t *lck, lck_grp_t *grp) { + return hw_lck_ptr_lock(lck, grp); // No optimization needed in this context +} + +void *hw_lck_ptr_lock_slow(hw_lck_ptr_t *lck, hw_lck_ptr_t tmp, + lck_grp_t *grp) { + return hw_lck_ptr_lock(lck, grp); // Same as fast path in this context +} + +// Simple Lock Functions +void usimple_lock(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->MutexLock(*sp); +} + +int usimple_lock_try(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*sp); +} + +void usimple_unlock(HyperMutex **sp) { + GetHypercallInterface()->MutexUnlock(*sp); + GetHypercallInterface()->Yield(); +} + +void usimple_lock_init(HyperMutex **sp) { + *sp = GetHypercallInterface()->AllocateMutex(); +} + +void usimple_lock_startup_init(HyperMutex ***sp) { + usimple_lock_init(*sp); +} + +// Miscellaneous Functions +void hw_lock_byte_init() { + abort(); +} + +void mutex_pause(uint32_t collisions) { + GetHypercallInterface()->Yield(); +} + +void host_lockgroup_info() {} + +void hw_wait_while_equals() { + abort(); +} + +void kdp_lck_mtx_find_owner() { + abort(); +} + +void kdp_lck_mtx_lock_spin_is_acquired() { + abort(); +} + +void kdp_lck_rw_lock_is_acquired_exclusive() { + abort(); +} + +void kdp_lck_spin_is_acquired() { + abort(); +} + +void kdp_rwlck_find_owner() { + abort(); +} + +void kdp_sleep_with_inheritor_find_owner() { + abort(); +} +} diff --git a/fuzz/xnu/osfmk/fakes/sync/mutex.cc b/fuzz/xnu/osfmk/fakes/sync/mutex.cc new file mode 100644 index 0000000..fff0c14 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/mutex.cc @@ -0,0 +1,113 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include + +#include "fuzz/host/hypercall/hypercall.h" + +extern "C" { +HyperMutex **lck_mtx_alloc_init() { + HyperMutex *mtx = GetHypercallInterface()->AllocateMutex(); + auto **ptr = new HyperMutex *; + *ptr = mtx; + return ptr; +} + +// We don't differentiate between spin- and fully-acquired mutexes +void lck_mtx_convert_spin() {} +void lck_mtx_free() { + abort(); +} + +// TODO(nedwill): this may need special semantics +void lck_mtx_lock_spin_always(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->MutexLock(*sp); +} + +void lck_mtx_sleep_with_inheritor() { + abort(); +} + +int lck_mtx_try_lock(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*sp); +} + +void lck_mtx_init_ext(HyperMutex **sp) { + *sp = GetHypercallInterface()->AllocateMutex(); +} + +void lck_mtx_try_lock_spin_always() { + abort(); +} + +void lck_mtx_init(HyperMutex **sp) { + *sp = GetHypercallInterface()->AllocateMutex(); +} + +void lck_mtx_startup_init(HyperMutex ***sp) { + lck_mtx_init(*sp); +} + +void lck_mtx_destroy(HyperMutex **sp) { + GetHypercallInterface()->FreeMutex(*sp); + *sp = nullptr; +} + +int lck_mtx_try_lock_spin(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*sp); +} + +void lck_mtx_lock(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->MutexLock(*sp); +} + +void lck_mtx_unlock(HyperMutex **sp) { + GetHypercallInterface()->MutexUnlock(*sp); + GetHypercallInterface()->Yield(); +} + +void lck_mtx_yield(HyperMutex **sp) { + GetHypercallInterface()->MutexUnlock(*sp); + GetHypercallInterface()->Yield(); + GetHypercallInterface()->MutexLock(*sp); +} + +void lck_mtx_lock_spin(HyperMutex **sp) { + lck_mtx_lock(sp); +} + +void lck_mtx_assert(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + // TODO(nedwill): debug this assertion + // GetHypercallInterface()->MutexAssertHeld(*sp); +} +} diff --git a/fuzz/xnu/osfmk/fakes/sync/rw_lock.cc b/fuzz/xnu/osfmk/fakes/sync/rw_lock.cc new file mode 100644 index 0000000..779d019 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/rw_lock.cc @@ -0,0 +1,178 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include + +#include "fuzz/host/hypercall/hypercall.h" + +struct lck_rw_t; + +extern "C" { + +HyperRwLock **lck_rw_alloc_init() { + HyperRwLock *mtx = GetHypercallInterface()->AllocateRwLock(); + auto **ptr = new HyperRwLock *; + *ptr = mtx; + return ptr; +} + +void lck_rw_destroy(HyperRwLock **sp) { + GetHypercallInterface()->FreeRwLock(*sp); + *sp = nullptr; +} + +void lck_rw_free() { + abort(); +} + +void lck_rw_init(HyperRwLock **sp) { + *sp = GetHypercallInterface()->AllocateRwLock(); +} + +void lck_rw_lock_exclusive_to_shared(HyperRwLock **sp) { + GetHypercallInterface()->RwLockExclusiveToShared(*sp); +} + +int lck_rw_lock_shared_to_exclusive(HyperRwLock **sp) { + return GetHypercallInterface()->RwLockSharedToExclusive(*sp); +} + +#define THREAD_WAITING -1 /* thread is waiting */ +#define THREAD_AWAKENED 0 /* normal wakeup */ +#define THREAD_TIMED_OUT 1 /* timeout expired */ +#define THREAD_INTERRUPTED 2 /* aborted/interrupted */ +#define THREAD_RESTART 3 /* restart operation entirely */ +#define THREAD_NOT_WAITING 10 /* thread didn't need to wait */ + +#define LCK_SLEEP_DEFAULT \ + 0x00 /* Release the lock while waiting for the event, then reclaim */ +/* RW locks are returned in the same mode */ +#define LCK_SLEEP_UNLOCK 0x01 /* Release the lock and return unheld */ +#define LCK_SLEEP_SHARED 0x02 /* Reclaim the lock in shared mode (RW only) */ +#define LCK_SLEEP_EXCLUSIVE \ + 0x04 /* Reclaim the lock in exclusive mode (RW only) */ +#define LCK_SLEEP_SPIN 0x08 /* Reclaim the lock in spin mode (mutex only) */ +#define LCK_SLEEP_PROMOTED_PRI 0x10 /* Sleep at a promoted priority */ +#define LCK_SLEEP_SPIN_ALWAYS \ + 0x20 /* Reclaim the lock in spin-always mode (mutex only) */ + +#define LCK_SLEEP_MASK 0x3f /* Valid actions */ + +typedef int lck_sleep_action_t; +typedef int wait_result_t; + +void lck_rw_startup_init(HyperRwLock ***sp) { + lck_rw_init(*sp); +} +int lck_rw_try_lock_exclusive(HyperRwLock **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->RwLockTryLockExclusive(*sp); +} +int lck_rw_try_lock_shared(HyperRwLock **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->RwLockTryLockShared(*sp); +} +void lck_rw_unlock() { + abort(); +} +void lck_rw_unlock_exclusive(HyperRwLock **sp) { + GetHypercallInterface()->RwLockUnlockExclusive(*sp); + GetHypercallInterface()->Yield(); +} +void lck_rw_unlock_shared(HyperRwLock **sp) { + GetHypercallInterface()->RwLockUnlockShared(*sp); + GetHypercallInterface()->Yield(); +} +void lck_rw_clear_promotion() { + abort(); +} + +int lck_rw_lock_exclusive_check_contended(HyperRwLock **sp) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->RwLockLockExclusive(*sp); + return GetHypercallInterface()->GetFuzzedBool(); +} + +int lck_rw_lock_yield_shared(HyperRwLock **sp, bool force_yield) { + // TODO(nedwill): sometimes return false + GetHypercallInterface()->RwLockUnlockShared(*sp); + GetHypercallInterface()->Yield(); + GetHypercallInterface()->RwLockLockShared(*sp); + return true; +} + +enum lck_rw_yield_t { + LCK_RW_YIELD_WRITERS_ONLY, + LCK_RW_YIELD_ANY_WAITER, + LCK_RW_YIELD_ALWAYS, +}; + +int lck_rw_lock_yield_exclusive(HyperRwLock **sp, lck_rw_yield_t mode) { + // TODO(nedwill): sometimes return false, handle modes + GetHypercallInterface()->RwLockUnlockExclusive(*sp); + GetHypercallInterface()->Yield(); + GetHypercallInterface()->RwLockLockExclusive(*sp); + return true; +} + +void lck_rw_assert(lck_rw_t *lck, unsigned int type) { + // TODO(nedwill): track the state of the lock and implement the assertions +} + +void lck_rw_lock_shared(HyperRwLock **lock) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->RwLockLockShared(*lock); +} + +#define LCK_RW_TYPE_SHARED 0x01 +#define LCK_RW_TYPE_EXCLUSIVE 0x02 + +unsigned int lck_rw_done(HyperRwLock **lock) { + unsigned int lock_type = + GetHypercallInterface()->RwLockIsShared(*lock) ? LCK_RW_TYPE_SHARED : LCK_RW_TYPE_EXCLUSIVE; + GetHypercallInterface()->RwLockUnlock(*lock); + GetHypercallInterface()->Yield(); + return lock_type; +} + +void lck_rw_lock_exclusive(HyperRwLock **lock) { + // GetHypercallInterface()->ThreadPrintf("lck_rw_lock_exclusive: %p\n", lock); + GetHypercallInterface()->Yield(); + GetHypercallInterface()->RwLockLockExclusive(*lock); +} + +void lck_rw_lock(HyperRwLock **sp, unsigned int lck_type) { + if (lck_type == LCK_RW_TYPE_SHARED) { + lck_rw_lock_shared(sp); + } else if (lck_type == LCK_RW_TYPE_EXCLUSIVE) { + lck_rw_lock_exclusive(sp); + } else { + abort(); + } +} +} diff --git a/fuzz/xnu/osfmk/fakes/sync/spin.cc b/fuzz/xnu/osfmk/fakes/sync/spin.cc new file mode 100644 index 0000000..8fd65e2 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/spin.cc @@ -0,0 +1,92 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include + +#include "fuzz/host/hypercall/hypercall.h" + +extern "C" { + +HyperMutex **lck_spin_alloc_init() { + HyperMutex *mtx = GetHypercallInterface()->AllocateMutex(); + auto **ptr = new HyperMutex *; + *ptr = mtx; + return ptr; +} + +void lck_spin_assert() {} + +void lck_spin_destroy(HyperMutex **sp) { + GetHypercallInterface()->FreeMutex(*sp); + *sp = nullptr; +} + +void lck_spin_init(HyperMutex **sp) { + *sp = GetHypercallInterface()->AllocateMutex(); +} + +void lck_spin_startup_init(HyperMutex ***sp) { + lck_spin_init(*sp); +} + +int lck_spin_try_lock(HyperMutex **sp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*sp); +} + +int lck_spin_try_lock_grp(HyperMutex **lock) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->MutexTryLock(*lock); +} + +void lck_spin_lock(HyperMutex **lock) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->MutexLock(*lock); + // GetHypercallInterface()->ThreadPrintf("lck_spin_lock: acquired lock %p\n", *lock); +} + +void lck_spin_unlock(HyperMutex **lock) { + GetHypercallInterface()->MutexUnlock(*lock); + // GetHypercallInterface()->ThreadPrintf("lck_spin_unlock: released lock %p\n", *lock); + GetHypercallInterface()->Yield(); +} + +void lck_spin_lock_grp(HyperMutex **lock) { + // printf("lck_spin_lock_grp: locking %p\n", lock); + GetHypercallInterface()->Yield(); + GetHypercallInterface()->MutexLock(*lock); +} + +void spinlock_timeout_NMI() { + abort(); +} + +void spinlock_timeout_panic() { + abort(); +} +} diff --git a/fuzz/xnu/osfmk/fakes/sync/tlock.cc b/fuzz/xnu/osfmk/fakes/sync/tlock.cc new file mode 100644 index 0000000..fbc851c --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/sync/tlock.cc @@ -0,0 +1,140 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +extern "C" { + +typedef void* lck_grp_t; + +// TODO(nedwill): ensure ticket-based fairness is used +void lck_ticket_assert_owned(HyperTicketLock** tlock) { + if (!GetHypercallInterface()->TicketLockHeld(tlock)) { + abort(); + } +} + +void lck_ticket_init(HyperTicketLock** tlock) { + GetHypercallInterface()->TicketLockAllocate(tlock); +} + +void lck_ticket_destroy(HyperTicketLock** tlock) { + GetHypercallInterface()->TicketLockDestroy(tlock, false); + *tlock = nullptr; +} + +void lck_ticket_lock(HyperTicketLock** tlock) { + GetHypercallInterface()->Yield(); + GetHypercallInterface()->TicketLockLock(tlock, false, -1, nullptr); +} + +void lck_ticket_unlock(HyperTicketLock** tlock) { + // GetHypercallInterface()->ThreadPrintf("lck_ticket_unlock: unlocking %p\n", sp); + GetHypercallInterface()->TicketLockUnlock(tlock); + GetHypercallInterface()->Yield(); +} + +void hw_lck_ticket_destroy(HyperTicketLock** tlock, lck_grp_t* grp) { + GetHypercallInterface()->TicketLockDestroy(tlock, true); +} + +bool hw_lck_ticket_held(HyperTicketLock** tlock) { + return GetHypercallInterface()->TicketLockHeld(tlock); +} + +void hw_lck_ticket_init(HyperTicketLock** tlock) { + GetHypercallInterface()->TicketLockAllocate(tlock); +} + +void hw_lck_ticket_init_locked(HyperTicketLock** tlock) { + hw_lck_ticket_init(tlock); + GetHypercallInterface()->TicketLockLock(tlock, false, -1, nullptr); +} + +void hw_lck_ticket_invalidate(HyperTicketLock** tlock) { + GetHypercallInterface()->TicketLockInvalidate(tlock); +} + +int hw_lck_ticket_lock_allow_invalid(HyperTicketLock** tlock, + uint64_t timeout, + void* handler, + lck_grp_t* grp) { + // TODO(nedwill): figure out when we can return contended + int const result = + GetHypercallInterface()->TicketLockLock(tlock, true, timeout, handler); + GetHypercallInterface()->Yield(); + return result; +} + +int hw_lck_ticket_lock_to(HyperTicketLock** tlock, uint64_t timeout, + void* handler, lck_grp_t* grp) { + // TODO(nedwill): figure out when we can return contended + int const result = + GetHypercallInterface()->TicketLockLock(tlock, false, timeout, handler); + GetHypercallInterface()->Yield(); + return result; +} + +bool hw_lck_ticket_lock_try(HyperTicketLock** tlock, lck_grp_t* grp) { + GetHypercallInterface()->Yield(); + return GetHypercallInterface()->TicketLockTryLock(tlock); +} + +void hw_lck_ticket_unlock(HyperTicketLock** tlock) { + GetHypercallInterface()->TicketLockUnlock(tlock); + GetHypercallInterface()->Yield(); +} + +void erase_all_test_mtx_stats() { + abort(); +} +void get_test_mtx_stats_string() { + abort(); +} +void lck_mtx_test_init() { + abort(); +} +void lck_mtx_test_mtx_contended() { + abort(); +} +void lck_mtx_test_mtx_contended_loop_time() { + abort(); +} +void lck_mtx_test_mtx_uncontended() { + abort(); +} +void lck_mtx_test_mtx_uncontended_loop_time() { + abort(); +} +void mpsc_test_pingpong() { + abort(); +} +} diff --git a/fuzz/xnu/osfmk/fakes/thread.c b/fuzz/xnu/osfmk/fakes/thread.c new file mode 100644 index 0000000..a0ef36b --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/thread.c @@ -0,0 +1,131 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/host/hypercall/hypercall.h" + +#include +#include +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +extern void *initproc; + +void free(void *ptr); +void *calloc(size_t nmemb, size_t size); + +void machine_stack_attach(thread_t thread, vm_offset_t stack) { + assert(stack); + thread->kernel_stack = stack; + thread_initialize_kernel_state(thread); +} + +void machine_thread_init() {} + +void XNUGetThreadInfo(thread_t thread, char *data, size_t size) { + char name[MAXTHREADNAMESIZE] = {}; + thread_get_thread_name(thread, name); + (void)snprintf(data, size, "%s", name); +} + +void machine_thread_create(thread_t thread, task_t task, bool first_thread) { + GetHypercallInterface()->CreateThread(thread, false); +} + +kern_return_t machine_thread_dup(thread_t self, thread_t target, + boolean_t is_corpse) { + // GetHypercallInterface()->CreateThread(target, (void*)self->continuation, self->parameter, + // false); It should already be created. We don't have anything else to copy. + return KERN_SUCCESS; +} + +bool ThreadLocked(thread_t thread) { + // nedwill: assumes hw_lck and ulock are just VirtualMutex under the hood + return hw_lck_ticket_held((hw_lck_ticket_t *)&thread->sched_lock); +} + +kern_return_t machine_thread_state_convert_from_user( + thread_t thread, thread_flavor_t flavor, thread_state_t tstate, + mach_msg_type_number_t count, thread_state_t old_tstate, + mach_msg_type_number_t old_count, thread_set_status_flags_t tssf_flags) { + // No conversion from userspace representation on this platform + return KERN_SUCCESS; +} + +ast_t cpu_pending_ast; +ast_t *ast_pending() { + return &cpu_pending_ast; +} + +void call_continuation(thread_continue_t continuation, void *parameter, + wait_result_t wresult, boolean_t enable_interrupts) { + GetHypercallInterface()->Abort(); +} + +thread_t machine_switch_context(thread_t old, thread_continue_t continuation, + thread_t new) { + GetHypercallInterface()->Abort(); +} + +void machine_thread_destroy(thread_t thread) { + // We implement simple locks OOL so we need to free them here + GetHypercallInterface()->FreeVirtualMutex(&thread->sched_lock); + GetHypercallInterface()->FreeVirtualMutex(&thread->wake_lock); + GetHypercallInterface()->FreeVirtualMutex(&thread->depress_timer->tc_lock); + GetHypercallInterface()->FreeVirtualMutex(&thread->wait_timer->tc_lock); + + // TODO(nwach): shouldn't the executor call this? + GetHypercallInterface()->NotifyDestroyed(thread); +} + +// TODO(nedwill): figure out a smarter way to deal with this +__attribute__((noreturn)) void thread_bootstrap_return() { + GetHypercallInterface()->ThreadPrintf("thread_bootstrap_return thread\n"); + // Keep init (launchd) alive permanently + if (get_bsdtask_info(current_task()) == initproc) { + while (true) { + GetHypercallInterface()->Block(); + } + } + + // TODO(nedwill): pull syscalls from main loop here + GetHypercallInterface()->ThreadFinished(); + __builtin_unreachable(); +} diff --git a/fuzz/xnu/osfmk/fakes/timer.c b/fuzz/xnu/osfmk/fakes/timer.c new file mode 100644 index 0000000..9ab25ee --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/timer.c @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" + +static mpqueue_head_t timer_queue; + +void cpu_init() { + timer_call_queue_init(&timer_queue); +} + +mpqueue_head_t *timer_queue_assign(uint64_t deadline) { + return &timer_queue; +} + +void timer_call_cpu(int cpu, void (*fn)(void *), void *arg) { + GetHypercallInterface()->Abort(); +} + +void timer_queue_cancel(mpqueue_head_t *queue, uint64_t deadline, + uint64_t new_deadline) {} + +mpqueue_head_t *timer_queue_cpu(int cpu) { + GetHypercallInterface()->Abort(); +} + +boolean_t timer_resort_threshold(uint64_t skew) { + GetHypercallInterface()->Abort(); +} + +void timer_call_nosync_cpu(int cpu, void (*fn)(void *), void *arg) {} + +void timer_resync_deadlines() {} + +void ml_timer_evaluate() {} + +boolean_t ml_timer_forced_evaluation() { + GetHypercallInterface()->Abort(); +} diff --git a/fuzz/xnu/osfmk/fakes/zalloc.c b/fuzz/xnu/osfmk/fakes/zalloc.c new file mode 100644 index 0000000..aa363b3 --- /dev/null +++ b/fuzz/xnu/osfmk/fakes/zalloc.c @@ -0,0 +1,1284 @@ +/* + * Copyright 2021 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "BUILD/obj/EXPORT_HDRS/osfmk/kern/zalloc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "osfmk/kern/kalloc.h" +#include "osfmk/kern/zalloc_internal.h" + +// int printf(const char *, ...) __printflike(1, 2); + +void free(void *ptr); + +// We link these in from libc/asan +void *malloc(size_t size); +void *calloc(size_t nmemb, size_t size); +int posix_memalign(void **memptr, size_t alignment, size_t size); + +struct zone *zinit(uintptr_t size, uintptr_t max, uintptr_t alloc, + const char *name) { + struct zone *zone = (struct zone *)calloc(1, sizeof(struct zone)); + zone->z_elem_size = size; + return zone; +} + +static void zone_initialize(zone_t zone, const char *name, vm_size_t size, + zone_create_flags_t flags) { + zone->z_self = zone; + zone->z_name = name; + zone->z_elem_size = size; +} + +zone_t zone_create(const char *name, vm_size_t size, + zone_create_flags_t flags) { + struct zone *zone = (struct zone *)calloc(1, sizeof(struct zone)); + zone_initialize(zone, name, size, flags); + return zone; +} + +// TODO(nedwill): validation here +void zone_change() {} + +// __attribute__((malloc)) __attribute__((overloadable)) extern void *zalloc( +// zone_or_view_t zone_or_view) { +// if (!zone_or_view.zov_view->zv_zone && +// zone_or_view.zov_kt_heap->kt_flags & KT_DATA_ONLY) { +// return calloc(1, zone_or_view.zov_kt_heap->kt_size); +// } +// return calloc(1, zone_or_view.zov_view->zv_zone->z_elem_size); +// } + +void zone_enable_caching(zone_t zone) {} + +__attribute__((always_inline)) void *xnu_zalloc(zone_t zov) { + return zalloc_flags(zov, Z_WAITOK); +} + +void xnu_zfree(zone_t zone, void *addr __unsafe_indexable) { + (void)zone; // Zone information is not used in this fake implementation + free(addr); +} + +__attribute__((noinline)) void zalloc_ro_mut(zone_id_t zid, void *elem, + vm_offset_t offset, + const void *new_data, + vm_size_t new_data_size) { + assert(zid >= ZONE_ID__FIRST_RO && zid <= ZONE_ID__LAST_RO); + + // #if ZSECURITY_CONFIG(READ_ONLY) + // zalloc_ro_mut_validate_src(zid, elem, (vm_offset_t)new_data, + // new_data_size); pmap_ro_zone_memcpy(zid, (vm_offset_t)elem, offset, + // (vm_offset_t)new_data, + // new_data_size); + // #else + (void)zid; + memcpy((void *)((uintptr_t)elem + offset), new_data, new_data_size); + // #endif +} + +void zalloc_ro_clear(zone_id_t zid, void *elem, vm_offset_t offset, + vm_size_t size) { + assert(zid >= ZONE_ID__FIRST_RO && zid <= ZONE_ID__LAST_RO); + // #if ZSECURITY_CONFIG(READ_ONLY) + // pmap_ro_zone_bzero(zid, (vm_offset_t)elem, offset, size); + // #else + (void)zid; + bzero((void *)((uintptr_t)elem + offset), size); + // #endif +} + +#undef zalloc_ro +extern void *zalloc_ro(zone_id_t zone_id, zalloc_flags_t flags) { + zone_t zone = &zone_array[zone_id]; + return zalloc(zone); +} + +__attribute__((malloc)) extern void *zalloc_noblock(zone_t zone_or_view) { + void *ptr = zalloc(zone_or_view); + // Cannot fail + if (!ptr) { + GetHypercallInterface()->Abort(); + } + return ptr; +} + +#undef zfree_ro +extern void zfree_ro(zone_id_t zone_id, void *addr __unsafe_indexable) { + (void)zone_id; + free(addr); +} + +int cpu_number() { + return 0; +} + +void *kalloc_canblock(size_t *psize, bool canblock, void *site) { + return malloc(*psize); +} + +static bool mb_is_ready = false; +extern unsigned char *mbutl; +extern unsigned char *embutl; +static size_t current_page = 0; + +uintptr_t kmem_mb_alloc(unsigned int mbmap, int size, int physContig, + int *err) { + *err = 0; + + if (!mb_is_ready) { + // 268 MB + *err = posix_memalign((void **)&mbutl, 4096, 4096 * 65535); + if (*err) { + return 0; + } + embutl = (unsigned char *)((uintptr_t)mbutl + (4096 * 65535)); + + mb_is_ready = true; + } + + assert(mbutl); + int pages = size / 4096; + uintptr_t ret = (uintptr_t)mbutl + (current_page * 4096); + current_page += pages; + + return ret; +} + +void *__MALLOC_ZONE(size_t size, int type, int flags, + vm_allocation_site_t *site) { + return malloc(size); +} + +void _FREE_ZONE(void *elem, size_t size, int type) { + free(elem); +} + +#undef kfree +void kfree(void *data, size_t size) { + free(data); +} + +void *realloc(void *ptr, size_t size); + +void *__REALLOC(void *addr, size_t size, int type, int flags, + vm_allocation_site_t *site) { + void *ptr = realloc(addr, size); + return ptr; +} + +void OSFree(void *ptr, uint32_t size, void *tag) { + free(ptr); +} + +void *OSMalloc(uint32_t size, void *tag) { + return malloc(size); +} + +struct kalloc_zone_cfg { + bool kzc_caching; + uint32_t kzc_size; + char kzc_name[MAX_ZONE_NAME]; +}; + +#define KZC_ENTRY(SIZE, caching) \ + { .kzc_caching = (caching), .kzc_size = (SIZE), .kzc_name = "kalloc." #SIZE } + +static SECURITY_READ_ONLY_LATE(struct kalloc_zone_cfg) k_zone_cfg[] = { +#if !defined(XNU_TARGET_OS_OSX) + +#if KALLOC_MINSIZE == 16 && KALLOC_LOG2_MINALIGN == 4 + /* Zone config for embedded 64-bit platforms */ + KZC_ENTRY(16, true), KZC_ENTRY(32, true), KZC_ENTRY(48, true), + KZC_ENTRY(64, true), KZC_ENTRY(80, true), KZC_ENTRY(96, true), + KZC_ENTRY(128, true), KZC_ENTRY(160, true), KZC_ENTRY(192, true), + KZC_ENTRY(224, true), KZC_ENTRY(256, true), KZC_ENTRY(288, true), + KZC_ENTRY(368, true), KZC_ENTRY(400, true), KZC_ENTRY(512, true), + KZC_ENTRY(576, false), KZC_ENTRY(768, false), KZC_ENTRY(1024, true), + KZC_ENTRY(1152, false), KZC_ENTRY(1280, false), KZC_ENTRY(1664, false), + KZC_ENTRY(2048, false), KZC_ENTRY(4096, false), KZC_ENTRY(6144, false), + KZC_ENTRY(8192, false), KZC_ENTRY(16384, false), KZC_ENTRY(32768, false), + +#elif KALLOC_MINSIZE == 8 && KALLOC_LOG2_MINALIGN == 3 + /* Zone config for embedded 32-bit platforms */ + KZC_ENTRY(8, true), KZC_ENTRY(16, true), KZC_ENTRY(24, true), + KZC_ENTRY(32, true), KZC_ENTRY(40, true), KZC_ENTRY(48, true), + KZC_ENTRY(64, true), KZC_ENTRY(72, true), KZC_ENTRY(88, true), + KZC_ENTRY(112, true), KZC_ENTRY(128, true), KZC_ENTRY(192, true), + KZC_ENTRY(256, true), KZC_ENTRY(288, true), KZC_ENTRY(384, true), + KZC_ENTRY(440, true), KZC_ENTRY(512, true), KZC_ENTRY(576, false), + KZC_ENTRY(768, false), KZC_ENTRY(1024, true), KZC_ENTRY(1152, false), + KZC_ENTRY(1280, false), KZC_ENTRY(1536, false), KZC_ENTRY(2048, false), + KZC_ENTRY(2128, false), KZC_ENTRY(3072, false), KZC_ENTRY(4096, false), + KZC_ENTRY(6144, false), KZC_ENTRY(8192, false), +/* To limit internal fragmentation, only add the following zones if the + * page size is greater than 4K. + * Note that we use ARM_PGBYTES here (instead of one of the VM macros) + * since it's guaranteed to be a compile time constant. + */ +#if ARM_PGBYTES > 4096 + KZC_ENTRY(16384, false), KZC_ENTRY(32768, false), +#endif /* ARM_PGBYTES > 4096 */ + +#else +#error missing or invalid zone size parameters for kalloc +#endif + +#else /* !defined(XNU_TARGET_OS_OSX) */ + + /* Zone config for macOS 64-bit platforms */ + KZC_ENTRY(16, true), KZC_ENTRY(32, true), KZC_ENTRY(48, true), + KZC_ENTRY(64, true), KZC_ENTRY(80, true), KZC_ENTRY(96, true), + KZC_ENTRY(128, true), KZC_ENTRY(160, true), KZC_ENTRY(192, true), + KZC_ENTRY(224, true), KZC_ENTRY(256, true), KZC_ENTRY(288, true), + KZC_ENTRY(368, true), KZC_ENTRY(400, true), KZC_ENTRY(512, true), + KZC_ENTRY(576, true), KZC_ENTRY(768, true), KZC_ENTRY(1024, true), + KZC_ENTRY(1152, false), KZC_ENTRY(1280, false), KZC_ENTRY(1664, false), + KZC_ENTRY(2048, true), KZC_ENTRY(4096, true), KZC_ENTRY(6144, false), + KZC_ENTRY(8192, true), +#if __x86_64__ + KZC_ENTRY(12288, false), +#endif /* __x86_64__ */ + KZC_ENTRY(16384, false), +#if __arm64__ + KZC_ENTRY(32768, false), +#endif +#endif /* !defined(XNU_TARGET_OS_OSX) */ +}; + +static SECURITY_READ_ONLY_LATE(struct kalloc_zone_cfg) k_zone_cfg_data[] = { + KZC_ENTRY(16, true), KZC_ENTRY(32, true), KZC_ENTRY(48, true), + KZC_ENTRY(64, true), KZC_ENTRY(96, true), KZC_ENTRY(128, true), + KZC_ENTRY(160, true), KZC_ENTRY(192, true), KZC_ENTRY(256, true), + KZC_ENTRY(368, true), KZC_ENTRY(512, true), KZC_ENTRY(768, false), + KZC_ENTRY(1024, true), KZC_ENTRY(1152, false), KZC_ENTRY(1664, false), + KZC_ENTRY(2048, false), KZC_ENTRY(4096, false), KZC_ENTRY(6144, false), + KZC_ENTRY(8192, false), KZC_ENTRY(16384, false), +#if __arm64__ + KZC_ENTRY(32768, false), +#endif +}; +#undef KZC_ENTRY + +#define MAX_K_ZONE(kzc) (uint32_t)(sizeof(kzc) / sizeof(kzc[0])) + +static SECURITY_READ_ONLY_LATE(zone_t) k_zone_default[MAX_K_ZONE(k_zone_cfg)]; +static SECURITY_READ_ONLY_LATE(zone_t) k_zone_data[MAX_K_ZONE(k_zone_cfg_data)]; +static SECURITY_READ_ONLY_LATE(zone_t) k_zone_kext[MAX_K_ZONE(k_zone_cfg)]; + +// static SECURITY_READ_ONLY_LATE(struct kheap_zones) kalloc_zones_default = { +// .cfg = k_zone_cfg, +// .heap_id = KHEAP_ID_DEFAULT, +// .k_zone = k_zone_default, +// .max_k_zone = MAX_K_ZONE(k_zone_cfg)}; +// SECURITY_READ_ONLY_LATE(struct kalloc_heap) +// KHEAP_DEFAULT[1] = {{ +// .kh_zones = &kalloc_zones_default, +// .kh_name = "default.", +// .kh_heap_id = KHEAP_ID_DEFAULT, +// }}; + +// static SECURITY_READ_ONLY_LATE(struct kheap_zones) kalloc_zones_data = { +// .cfg = k_zone_cfg_data, +// .heap_id = KHEAP_ID_DATA_BUFFERS, +// .k_zone = k_zone_data, +// .max_k_zone = MAX_K_ZONE(k_zone_cfg_data)}; +// SECURITY_READ_ONLY_LATE(struct kalloc_heap) +// KHEAP_DATA_BUFFERS[1] = {{ +// .kh_zones = &kalloc_zones_data, +// .kh_name = "data.", +// .kh_heap_id = KHEAP_ID_DATA_BUFFERS, +// }}; + +// SECURITY_READ_ONLY_LATE(struct kalloc_heap) ZV_NAMEI[1] = { +// { +// .kh_zones = &kalloc_zones_data, +// .kh_name = "vfs.namei.", +// .kh_heap_id = KHEAP_ID_DATA_BUFFERS, +// } +// }; + +// TODO(nedwill): figure out why this isn't working +// we are returning size 0 here when calling malloc on this. +// it might be a failure to initialize kalloc's KHEAP_ID_DATA_BUFFERS properly +ZONE_VIEW_DEFINE(ZV_NAMEI, "vfs.namei", KHEAP_ID_DATA_BUFFERS, 4096); + +static SECURITY_READ_ONLY_LATE(vm_map_t) kernel_data_map; +static SECURITY_READ_ONLY_LATE(vm_map_t) kalloc_large_map; +static SECURITY_READ_ONLY_LATE(vm_map_t) kalloc_large_data_map; + +// __startup_data size_t kheap_zsize[KHEAP_NUM_ZONES]; + +// __startup_func static void kalloc_zone_init(const char *kheap_name, +// zone_kheap_id_t kheap_id, +// zone_id_t *kheap_zstart, +// zone_create_flags_t zc_flags) { +// printf("kheap_name: %s, kheap_id: %u\n", kheap_name, kheap_id); +// zc_flags |= (ZC_KASAN_NOREDZONE | ZC_KASAN_NOQUARANTINE | ZC_PGZ_USE_GUARDS); + +// for (uint32_t i = 0; i < KHEAP_NUM_ZONES; i++) { +// uint32_t size = (uint32_t)kheap_zsize[i]; +// char buf[MAX_ZONE_NAME], *z_name; +// int len; + +// len = scnprintf(buf, MAX_ZONE_NAME, "%s.%u", kheap_name, size); +// z_name = zalloc_permanent(len + 1, ZALIGN_NONE); +// strlcpy(z_name, buf, len + 1); + +// (void)zone_create_ext(z_name, size, zc_flags, ZONE_ID_ANY, ^(zone_t z) { +// printf("Inside extra_setup: z: %p, zone_index: %u\n", z, zone_index(z)); +// #if __arm64e__ +// uint32_t scale = kalloc_log2down(size / 32); + +// if (size == 32 << scale) { +// z->z_array_size_class = scale; +// } else { +// z->z_array_size_class = scale | 0x10; +// } +// #endif +// zone_security_array[zone_index(z)].z_kheap_id = (uint16_t)kheap_id; +// if (i == 0) { +// *kheap_zstart = zone_index(z); +// } +// }); +// } +// } + +// __startup_func static void kalloc_heap_init(struct kalloc_heap *kheap) { +// kalloc_zone_init("kalloc", kheap->kh_heap_id, &kheap->kh_zstart, ZC_NONE); +// /* +// * Count all the "raw" views for zones in the heap. +// */ +// zone_view_count += KHEAP_NUM_ZONES; +// } + +// SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_DEFAULT[1] = { +// { +// .kh_name = "default.", +// .kh_heap_id = KHEAP_ID_DEFAULT, +// .kh_tag = VM_KERN_MEMORY_KALLOC +// } +// }; + +// SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_DATA_BUFFERS[1] = { +// { +// .kh_name = "data.", +// .kh_heap_id = KHEAP_ID_DATA_BUFFERS, +// .kh_tag = VM_KERN_MEMORY_KALLOC_DATA, +// } +// }; + +// vm_size_t kalloc_next_good_size( +// vm_size_t size, +// uint32_t period) { +// GetHypercallInterface()->Abort(); +// } + +// struct kalloc_result __kalloc_array_decode( +// vm_address_t array) { +// GetHypercallInterface()->Abort(); +// } + +// __startup_func static void kalloc_zsize_compute(void) { +// size_t step = KHEAP_STEP_START; +// uint32_t start = 0; +// /* +// * Manually initialize extra initial zones +// */ +// kheap_zsize[start] = 16; +// kheap_zsize[start + 1] = KHEAP_START_SIZE; + +// /* +// * Compute sizes for remaining zones +// */ +// for (uint32_t i = 0; i < KHEAP_NUM_STEPS; i++) { +// uint32_t step_idx = (i * 2) + KHEAP_EXTRA_ZONES; +// kheap_zsize[step_idx] = kheap_zsize[step_idx - 1] + step; +// kheap_zsize[step_idx + 1] = kheap_zsize[step_idx] + step; +// step *= 2; +// } +// } + +// __startup_func static void kalloc_init(void) { +// static_assert(KHEAP_MAX_SIZE >= KALLOC_SAFE_ALLOC_SIZE); +// kalloc_zsize_compute(); + +// /* Initialize kalloc default heap */ +// kalloc_heap_init(KHEAP_DEFAULT); + +// #if ZSECURITY_CONFIG(SUBMAP_USER_DATA) +// /* Initialize kalloc data buffers heap */ +// kalloc_heap_init(KHEAP_DATA_BUFFERS); +// #else +// *KHEAP_DATA_BUFFERS = *KHEAP_DEFAULT; +// #endif +// } +// STARTUP(ZALLOC, STARTUP_RANK_THIRD, kalloc_init); + +// #undef kheap_free +// extern void kheap_free(kalloc_heap_t heap, void *data, vm_size_t size) { +// free(data); +// } + +__startup_func void zone_create_startup(struct zone_create_startup_spec *spec) { + zone_t z; + + z = zone_create_ext(spec->z_name, spec->z_size, spec->z_flags, spec->z_zid, + spec->z_setup); + if (spec->z_var) { + *spec->z_var = z; + } +} + +_Atomic uint32_t bt_init_flag = 0; + +// #undef kalloc_ext +// struct kalloc_result kalloc_ext(void *kheap_or_kt_view, vm_size_t req_size, +// zalloc_flags_t flags, void *site) { +// if (req_size > (vm_size_t)65535 * 2) { +// return (struct kalloc_result){.addr = NULL, .size = 0}; +// } +// void *addr = malloc(req_size); +// if (flags & Z_ZERO) { +// bzero(addr, req_size); +// } +// return (struct kalloc_result){.addr = addr, .size = req_size}; +// } + +#undef zalloc_flags +void *zalloc_flags(zone_t zone, zalloc_flags_t flags) { + zone_t self = zone->z_self; + zone_stats_t zstats = zone->z_stats; + + assert(self != NULL); + // Add any other necessary assertions + + return zalloc_ext(self, zstats, flags).addr; +} + +#undef kheap_free_addr +void kheap_free_addr(kalloc_heap_t heap, void *addr) { + free(addr); +} + +#undef zalloc_permanent +void *zalloc_permanent(vm_size_t size, vm_offset_t mask) { + return calloc(1, size); +} + +static zone_t zone_init_defaults(zone_id_t zid) { + zone_t z = &zone_array[zid]; + + // No need to set z_wired_max and collectable as they are + // not used in the fake implementation. + + // We don't need locks for libc malloc/free, so remove the + // hw_lck_ticket_init calls. + + // zone_depot_init is not necessary for libc malloc/free. + + return z; +} + +static zone_id_t num_zones_in_use; +zone_id_t _Atomic num_zones; + +static zone_t zone_create_find(const char *name, vm_size_t size, + zone_create_flags_t flags, + zone_id_t *zid_inout) { + zone_id_t nzones, zid = *zid_inout; + zone_t z; + + nzones = (zone_id_t)os_atomic_load(&num_zones, relaxed); + assert(num_zones_in_use <= nzones && nzones < MAX_ZONES); + + if (__improbable(nzones < ZONE_ID__FIRST_DYNAMIC)) { + /* + * The first time around, make sure the reserved zone IDs + * have an initialized lock as zone_index_foreach() will + * enumerate them. + */ + while (nzones < ZONE_ID__FIRST_DYNAMIC) { + zone_init_defaults(nzones++); + } + + os_atomic_store(&num_zones, nzones, release); + } + + if (zid != ZONE_ID_ANY) { + if (zid >= ZONE_ID__FIRST_DYNAMIC) { + panic("zone_create: invalid desired zone ID %d for %s", zid, name); + } + if (flags & ZC_DESTRUCTIBLE) { + panic("zone_create: ID %d (%s) must be permanent", zid, name); + } + if (zone_array[zid].z_self) { + panic("zone_create: creating zone ID %d (%s) twice", zid, name); + } + z = &zone_array[zid]; + } else { + zid = nzones++; + z = zone_init_defaults(zid); + + /* + * The release barrier pairs with the acquire in + * zone_index_foreach() and makes sure that enumeration loops + * always see an initialized zone lock. + */ + os_atomic_store(&num_zones, nzones, release); + } + +out: + num_zones_in_use++; + + *zid_inout = zid; + return z; +} + +#define ZONE_MAX_ALLOC_SIZE (32 * 1024) +#define ZONE_MIN_ELEM_SIZE sizeof(uint64_t) +#define ZONE_ALIGN_SIZE ZONE_MIN_ELEM_SIZE + +static vm_size_t zone_elem_adjust_size(const char *name __unused, + vm_size_t elem_size, + zone_create_flags_t flags __unused, + uint32_t *redzone __unused) { + vm_size_t size; + /* + * Adjust element size for minimum size and pointer alignment + */ + size = (elem_size + ZONE_ALIGN_SIZE - 1) & -ZONE_ALIGN_SIZE; + if (size < ZONE_MIN_ELEM_SIZE) { + size = ZONE_MIN_ELEM_SIZE; + } + + // #if KASAN_ZALLOC + // /* + // * Expand the zone allocation size to include the redzones. + // * + // * For page-multiple zones add a full guard page because they + // * likely require alignment. + // */ + // uint32_t redzone_tmp; + // if (flags & (ZC_KASAN_NOREDZONE | ZC_PERCPU)) { + // redzone_tmp = 0; + // } else if ((size & PAGE_MASK) == 0) { + // if (size != PAGE_SIZE && (flags & ZC_ALIGNMENT_REQUIRED)) { + // panic("zone_create: zone %s can't provide more than PAGE_SIZE" + // "alignment", name); + // } + // redzone_tmp = PAGE_SIZE; + // } else if (flags & ZC_ALIGNMENT_REQUIRED) { + // redzone_tmp = 0; + // } else { + // redzone_tmp = KASAN_GUARD_SIZE; + // } + // size += redzone_tmp * 2; + // if (redzone) { + // *redzone = redzone_tmp; + // } + // #endif + return size; +} + +static vm_size_t zone_get_min_alloc_granule(vm_size_t elem_size, + zone_create_flags_t flags) { + vm_size_t alloc_granule = PAGE_SIZE; + if (flags & ZC_PERCPU) { + alloc_granule = PAGE_SIZE * zpercpu_count(); + if (PAGE_SIZE % elem_size > 256) { + panic("zone_create: per-cpu zone has too much fragmentation"); + } + } else if (flags & ZC_READONLY) { + alloc_granule = PAGE_SIZE; + } else if ((elem_size & PAGE_MASK) == 0) { + /* zero fragmentation by definition */ + alloc_granule = elem_size; + } else if (alloc_granule % elem_size == 0) { + /* zero fragmentation by definition */ + } else { + vm_size_t frag = (alloc_granule % elem_size) * 100 / alloc_granule; + vm_size_t alloc_tmp = PAGE_SIZE; + vm_size_t max_chunk_size = ZONE_MAX_ALLOC_SIZE; + +#if __arm64__ + /* + * Increase chunk size to 48K for sizes larger than 4K on 16k + * machines, so as to reduce internal fragementation for kalloc + * zones with sizes 12K and 24K. + */ + if (elem_size > 4 * 1024 && PAGE_SIZE == 16 * 1024) { + max_chunk_size = 48 * 1024; + } +#endif + while ((alloc_tmp += PAGE_SIZE) <= max_chunk_size) { + vm_size_t frag_tmp = (alloc_tmp % elem_size) * 100 / alloc_tmp; + if (frag_tmp < frag) { + frag = frag_tmp; + alloc_granule = alloc_tmp; + } + } + } + return alloc_granule; +} + +static inline bool zone_has_index(zone_t z, zone_id_t zid) { + return zone_array + zid == z; +} + +__startup_data static struct zone_stats zone_stats_startup[MAX_ZONES]; + +zone_t zone_create_ext(const char *name, vm_size_t size, + zone_create_flags_t flags, zone_id_t zid, + void (^extra_setup)(zone_t)) { + vm_size_t alloc; + uint32_t redzone; + zone_t z; + zone_security_flags_t *zsflags; + + if (size > ZONE_MAX_ALLOC_SIZE) { + panic("zone_create: element size too large: %zd", (size_t)size); + } + + if (size < 2 * sizeof(vm_size_t)) { + /* Elements are too small for kasan. */ + flags |= ZC_KASAN_NOQUARANTINE | ZC_KASAN_NOREDZONE; + } + + size = zone_elem_adjust_size(name, size, flags, &redzone); + /* + * Allocate the zone slot, return early if we found an older match. + */ + z = zone_create_find(name, size, flags, &zid); + if (__improbable(z->z_self)) { + /* We found a zone to reuse */ + return z; + } + + /* + * Initialize the zone properly. + */ + + /* + * If the kernel is post lockdown, copy the zone name passed in. + * Else simply maintain a pointer to the name string as it can only + * be a core XNU zone (no unloadable kext exists before lockdown). + */ + if (startup_phase >= STARTUP_SUB_LOCKDOWN) { + size_t nsz = MIN(strlen(name) + 1, MACH_ZONE_NAME_MAX_LEN); + char *buf = zalloc_permanent(nsz, ZALIGN_NONE); + strlcpy(buf, name, nsz); + z->z_name = buf; + } else { + z->z_name = name; + } + if (__probable(zone_array[ZONE_ID_PERCPU_PERMANENT].z_self)) { + z->z_stats = zalloc_percpu_permanent_type(struct zone_stats); + } else { + /* + * zone_init() hasn't run yet, use the storage provided by + * zone_stats_startup(), and zone_init() will replace it + * with the final value once the PERCPU zone exists. + */ + z->z_stats = __zpcpu_mangle_for_boot(&zone_stats_startup[zone_index(z)]); + } + + alloc = zone_get_min_alloc_granule(size, flags); + + z->z_elem_size = (uint16_t)size; + z->z_chunk_pages = (uint16_t)atop(alloc); + z->z_quo_magic = Z_MAGIC_QUO(size); + z->z_align_magic = Z_MAGIC_ALIGNED(size); + if (flags & ZC_PERCPU) { + z->z_chunk_elems = (uint16_t)(PAGE_SIZE / z->z_elem_size); + z->z_elem_offs = (uint16_t)(PAGE_SIZE % z->z_elem_size); + } else { + z->z_chunk_elems = (uint16_t)(alloc / z->z_elem_size); + z->z_elem_offs = (uint16_t)(alloc % z->z_elem_size); + } + + /* + * Handle KPI flags + */ + zsflags = &zone_security_array[zid]; + + /* Initialize zone_security_array entry */ + memset(zsflags, 0, sizeof(*zsflags)); + + /* ZC_CACHING applied after all configuration is done */ + if (flags & ZC_NOCACHING) { + z->z_nocaching = true; + } + + /* + * Then if there's extra tuning, do it + */ + if (extra_setup) { + extra_setup(z); + } + z->z_self = z; + return z; +} + +__startup_func static void zone_security_array_init(void) { + struct zone_security_flags default_config = { + .z_kheap_id = KHEAP_ID_NONE, + .z_noencrypt = false, + .z_submap_idx = Z_SUBMAP_IDX_GENERAL_0, + .z_kalloc_type = false, + }; + // if (ZSECURITY_OPTIONS_SEQUESTER & zsecurity_options) { + // default_config.z_va_sequester = true; + // } + for (zone_id_t i = 0; i < MAX_ZONES; i++) { + zone_security_array[i] = default_config; + } +} + +#define MAX_ZONES 690 +struct zone zone_array[MAX_ZONES]; +SECURITY_READ_ONLY_LATE(zone_security_flags_t) zone_security_array[MAX_ZONES]; + +void zone_bootstrap() {} +void zone_cram_foreign(zone_t zone, vm_offset_t newmem, vm_size_t size) {} +vm_size_t zone_element_info(void *addr, vm_tag_t *ptag) { + GetHypercallInterface()->Abort(); +} +void zone_fill_initially(zone_t zone, vm_size_t nelems) {} +extern vm_offset_t zone_foreign_mem_init(vm_size_t size, + bool allow_meta_steal) { + return 0; +} +// TODO(nedwill): implement GC +void zone_gc(zone_gc_level_t level) {} + +vm_size_t zone_get_foreign_alloc_size(const char *name __unused, + vm_size_t elem_size, + zone_create_flags_t flags, + uint16_t min_pages) { + return 0; +} + +const char *zone_heap_name(zone_t zone) { + GetHypercallInterface()->Abort(); +} +void zone_id_require(zone_id_t zone_id, vm_size_t elem_size, void *addr) {} +void zone_id_require_allow_foreign(zone_id_t zone_id, vm_size_t elem_size, + void *addr) {} +uint32_t zone_index_from_tag_index(uint32_t tag_zone_index, + vm_size_t *elem_size) { + GetHypercallInterface()->Abort(); +} + +// TODO(nedwill): return true sometimes +bool zone_map_nearing_exhaustion(void) { + return false; +} +bool zone_maps_owned(vm_address_t addr, vm_size_t size) { + return false; +} + +void zone_map_sizes(vm_map_size_t *psize, vm_map_size_t *pfree, + vm_map_size_t *plargest_free) { + GetHypercallInterface()->Abort(); +} +const char *zone_name(zone_t zone) { + GetHypercallInterface()->Abort(); +} +// Creates a thread to replenish the zone +void zone_replenish_configure() {} +void zone_require(zone_t zone, void *addr) {} +void zone_require_ro(zone_id_t zone_id, vm_size_t elem_size, void *addr) {} +void zone_set_exhaustible(zone_t zone, vm_size_t max_elements, + bool exhausts_by_design) {} +void zone_set_submap_idx() {} +uint32_t zone_view_count = 0; + +ZONE_DEFINE_TYPE(percpu_u64_zone, "percpu.64", uint64_t, + ZC_PERCPU | ZC_ALIGNMENT_REQUIRED | ZC_KASAN_NOREDZONE); + +void zone_raise_reserve(union zone_or_view zov, uint16_t min_elements) { + zone_t zone = zov.zov_zone; + + if (zone < zone_array || zone > &zone_array[MAX_ZONES]) { + zone = zov.zov_view->zv_zone; + } else { + zone = zov.zov_zone; + } + + printf("[zone_raise_reserve]: zov.zov_zone: %p, zov.zov_view: %p, zone: %p\n", + zov.zov_zone, zov.zov_view, zone); + + os_atomic_max(&zone->z_elems_rsv, min_elements, relaxed); +} + +void zone_id_require_aligned(zone_id_t zone_id, void *addr) {} + +// static zone_t kalloc_zone_for_size_with_flags(zone_id_t zid, vm_size_t size, +// zalloc_flags_t flags) { +// vm_size_t max_size = KHEAP_MAX_SIZE; +// bool forcopyin = flags & Z_MAY_COPYINMAP; + +// if (forcopyin) { +// #if __x86_64__ +// /* +// * On Intel, the OSData() ABI used to allocate +// * from the kernel map starting at PAGE_SIZE. +// * +// * If only vm_map_copyin() or a wrapper is used, +// * then everything will work fine because vm_map_copy_t +// * will perform an actual copy if the data is smaller +// * than msg_ool_size_small (== KHEAP_MAX_SIZE). +// * +// * However, if anyone is trying to call mach_vm_remap(), +// * then bad things (TM) happen. +// * +// * Avoid this by preserving the ABI and moving +// * to kalloc_large() earlier. +// * +// * Any recent code really ought to use IOMemoryDescriptor +// * for this purpose however. +// */ +// max_size = PAGE_SIZE - 1; +// #endif +// } + +// if (size <= max_size) { +// uint32_t idx; + +// if (size <= KHEAP_START_SIZE) { +// zid += (size > 16); +// } else { +// /* +// * . log2down(size - 1) is log2up(size) - 1 +// * . (size - 1) >> (log2down(size - 1) - 1) is either 0x2 or 0x3 +// */ +// size -= 1; +// idx = kalloc_log2down((uint32_t)size); +// zid += KHEAP_EXTRA_ZONES + 2 * (idx - KHEAP_START_IDX) + +// ((uint32_t)size >> (idx - 1)) - 2; +// // Print intermediate variables and final zid value +// printf("size: %u, idx: %u, zid: %u\n", size, idx, zid); +// } +// if (zid > MAX_ZONES) { +// printf("kalloc_zone_for_size(%lu) => %u (out of bounds)\n", size, zid); +// GetHypercallInterface()->Abort(); +// } +// // Print the calculated zone index and resulting zone pointer +// printf("Calculated zid: %u\n", zid); +// zone_t result_zone = zone_array + zid; +// printf("Returning zone pointer: %p\n", (void *)result_zone); +// return result_zone; +// } + +// // Print when returning ZONE_NULL +// printf("Returning ZONE_NULL\n"); +// return ZONE_NULL; +// } + +bool panic_include_kalloc_types = false; + +// zone_t kalloc_zone_for_size(zone_id_t zid, size_t size) { +// return kalloc_zone_for_size_with_flags(zid, size, Z_WAITOK); +// } + +void zfree_ext(zone_t zone, zone_stats_t zstats, void *addr, + uint64_t combined_size) { + GetHypercallInterface()->Abort(); +} + +vm_size_t zone_element_size(void *elem, zone_t *z, bool clear_oob, + vm_offset_t *oob_offs) { + return 0; +} + +zone_t kalloc_type_src_zone = ZONE_NULL; + +struct kalloc_result zalloc_ext(zone_t zone, zone_stats_t zstats, + zalloc_flags_t flags) { + struct kalloc_result result; + vm_size_t size = zone->z_elem_size; + + // Check for explicit zero-init request or if the zone is configured for clean memory + // ZC_ZFREE_CLEARMEM doesn't seem to be published so we have to stay on the safe side + // and use zero-initialized memory for all zones. We should revisit this if we find + // a way to determine if a zone is configured for clean memory or not or they publish + // their internal code properly. + result.addr = calloc(1, size); + + result.size = size; + + return result; +} + +SECURITY_READ_ONLY_LATE(zone_cache_ops_t) zcache_ops[ZONE_ID__FIRST_DYNAMIC]; + +__startup_func void zone_view_startup_init( + struct zone_view_startup_spec *spec) { + struct kalloc_heap *heap = NULL; + zone_view_t zv = spec->zv_view; + zone_t z; + zone_security_flags_t zsflags; + printf("Initializing zone view %s\n", zv->zv_name); + + switch (spec->zv_heapid) { + case KHEAP_ID_DATA_BUFFERS: + heap = KHEAP_DATA_BUFFERS; + break; + default: + heap = NULL; + } + + if (heap) { + z = kalloc_zone_for_size(heap->kh_zstart, spec->zv_size); + printf("Initializing zone for heap %s with size %zu\n", + spec->zv_view->zv_name, spec->zv_size); + if (spec->zv_size > zone_elem_inner_size(z)) { + GetHypercallInterface()->Abort(); + } + printf("Returned zone %p has size %u\n", z, z->z_elem_size); + } else { + z = *spec->zv_zone; + // if (z->z_elem_size == 0) { + // z->z_elem_size = spec->zv_size; + // } + printf("Initializing zone %s with size %zu\n", spec->zv_view->zv_name, + spec->zv_size); + if (spec->zv_size > zone_elem_inner_size(z)) { + GetHypercallInterface()->Abort(); + } + printf("Returned zone %p has size %u\n", z, z->z_elem_size); + } + + assert(z); + if (!z) { + panic("%s: failed to find zone for zone view %s", __func__, zv->zv_name); + } + + zv->zv_zone = z; + zv->zv_stats = zalloc_percpu_permanent_type(struct zone_stats); + zv->zv_next = z->z_views; // Link to existing views + zsflags = zone_security_config(z); + + if (z->z_views == NULL && zsflags.z_kheap_id == KHEAP_ID_NONE) { + zone_view_count += 2; + } else { + zone_view_count += 1; + } + + z->z_views = zv; +} + +void zalloc_first_proc_made() {} + +#undef zalloc_percpu +void *zalloc_percpu(zone_or_view_t zov, zalloc_flags_t flags) { + return zalloc(zov.zov_view->zv_zone); +} + +void zfree_percpu(zone_or_view_t zone_or_view, void *addr) { + free(addr); +} + +void *zalloc_percpu_permanent(vm_size_t size, vm_offset_t align_mask) { + return calloc(1, size); +} + +#define INDEX_ZDLUT(size) (((size) + KALLOC_MINALIGN - 1) / KALLOC_MINALIGN) +#define MAX_SIZE_ZDLUT ((KALLOC_DLUT_SIZE - 1) * KALLOC_MINALIGN) + +// bool kalloc_owned_map(vm_map_t map) { +// return map && (map == kalloc_large_map || map == kalloc_large_data_map || +// map == kernel_data_map); +// } + +// #undef krealloc_ext +// struct kalloc_result krealloc_ext(void *kheap_or_kt_view, void *addr, +// vm_size_t old_size, vm_size_t new_size, +// zalloc_flags_t flags, void *site) { +// vm_size_t old_bucket_size, new_bucket_size, min_size; +// vm_size_t adj_new_size, adj_old_size; +// struct kalloc_result kr; + +// if (new_size == 0) { +// free(addr); +// return (struct kalloc_result){}; +// } + +// if (addr == NULL) { +// return kalloc_ext(kheap_or_kt_view, new_size, flags, site); +// } + +// adj_old_size = old_size; +// adj_new_size = new_size; + +// kr.addr = calloc(1, new_size); +// kr.size = new_size; +// return kr; +// } + +void *zalloc_permanent_tag(vm_size_t size, vm_offset_t mask, vm_tag_t tag) { + return calloc(1, size); +} + +#undef zcache_alloc_n +zstack_t zcache_alloc_n(zone_id_t zone_id, uint32_t count, zalloc_flags_t flags, zone_cache_ops_t ops) { + zstack_t stack = {0}; + zone_t zone = zone_by_id(zone_id); + + if (zone == NULL) { + return stack; + } + + for (uint32_t i = 0; i < count; i++) { + void *elem = NULL; + if (ops->zc_op_alloc) { + elem = ops->zc_op_alloc(zone_id, flags); + } else { + // Fallback to using calloc if no alloc operation is provided + elem = calloc(1, zone->z_elem_size); + } + + if (elem == NULL) { + // Free previously allocated elements + while (!zstack_empty(stack)) { + void *to_free = zstack_pop(&stack); + if (ops->zc_op_free) { + ops->zc_op_free(zone_id, to_free); + } else { + free(to_free); + } + } + return stack; + } + + if (ops->zc_op_mark_valid) { + ops->zc_op_mark_valid(zone_id, elem); + } + + zstack_push(&stack, elem); + } + + return stack; +} + +#undef zcache_free_n +void zcache_free_n(zone_id_t zone_id, zstack_t stack, zone_cache_ops_t ops) { + while (!zstack_empty(stack)) { + void *elem = zstack_pop(&stack); + if (ops->zc_op_mark_invalid) { + ops->zc_op_mark_invalid(zone_id, elem); + } + if (ops->zc_op_free) { + ops->zc_op_free(zone_id, elem); + } else { + free(elem); + } + } +} + +void *__unsafe_indexable zcache_mark_valid(zone_t zone, + void *elem __unsafe_indexable) { + return elem; +} + +void zstack_push(zstack_t *stack, void *elem) { + vm_offset_t *new_elements = realloc((void *)stack->z_head, (stack->z_count + 1) * sizeof(vm_offset_t)); + if (new_elements) { + new_elements[stack->z_count] = (vm_offset_t)elem; + stack->z_head = (vm_offset_t)new_elements; + stack->z_count++; + } + // If realloc fails, we simply don't add the new element, preserving the existing stack +} + +void *zstack_pop(zstack_t *stack) { + if (zstack_empty(*stack)) { + return NULL; + } + + vm_offset_t *elements = (vm_offset_t *)stack->z_head; + void *elem = (void *)elements[--stack->z_count]; + + if (stack->z_count == 0) { + free(elements); + stack->z_head = 0; + } + + return elem; +} + +zstack_t zalloc_n(zone_id_t zone_id, uint32_t count, zalloc_flags_t flags) { + zstack_t stack = {0}; + zone_t zone = zone_by_id(zone_id); + + if (zone == NULL) { + return stack; + } + + size_t elem_size = zone->z_elem_size; + + for (uint32_t i = 0; i < count; i++) { + void *elem = NULL; + + // Simulate allocation based on flags + if (flags & Z_NOFAIL) { + elem = calloc(1, elem_size); // Always succeed + } else { + // Simulate occasional allocation failure + if (GetHypercallInterface()->GetFuzzedUint32InRange(0, 9) == 0) { // 90% success rate + elem = calloc(1, elem_size); + } + } + + if (elem == NULL) { + // Stop allocation if we couldn't allocate an element + break; + } + + zstack_push(&stack, elem); + } + + return stack; +} + +#undef zfree_nozero_n +void zfree_nozero_n(zone_id_t zone_id, zstack_t stack) { + void *elem = NULL; + + while ((elem = zstack_pop(&stack)) != NULL) { + // In a real implementation, you might want to add the element back to the zone + // For now, we'll just free it + free(elem); + } + + // The stack array itself is freed in zstack_pop when the last element is removed +} + +void *__unsafe_indexable zcache_mark_invalid(zone_t zone, + void *elem __unsafe_indexable) { + return elem; +} + +void kfree_type_impl_internal(kalloc_type_view_t kt_view, + void *ptr __unsafe_indexable) { + (void)kt_view; // Unused parameter since we don't manage memory ourselves + free(ptr); +} + +// void kfree_ext(void *kheap_or_kt_view, void *addr __unsafe_indexable, +// vm_size_t size) { +// free(addr); +// } + +// void kfree_addr_ext(kalloc_heap_t kheap, void *addr __unsafe_indexable) { +// free(addr); +// } + +zone_id_t zone_id_for_element(void *addr, vm_size_t esize) { + GetHypercallInterface()->Abort(); +} + +vm_map_size_t zone_map_size; + +__startup_func void zone_cram_early(zone_t zone, vm_offset_t newmem, + vm_size_t size) {} + +vm_size_t zone_get_early_alloc_size(const char *name __unused, + vm_size_t elem_size, + zone_create_flags_t flags, + vm_size_t min_elems) { + return 0; +} + +__startup_func vm_offset_t zone_early_mem_init(vm_size_t size) { + return 0; +} + +__attribute__((noinline)) uint64_t zalloc_ro_mut_atomic(zone_id_t zid, + void *elem, + vm_offset_t offset, + zro_atomic_op_t op, + uint64_t value) { + assert(zid >= ZONE_ID__FIRST_RO && zid <= ZONE_ID__LAST_RO); + + (void)zid; + return __zalloc_ro_mut_atomic((vm_offset_t)elem + offset, op, value); +} + +#undef zalloc_id +__attribute__((always_inline)) void *zalloc_id(zone_id_t zid, + zalloc_flags_t flags) { + return zalloc_flags(&zone_array[zid], flags); +} + +#undef zfree_id +void zfree_id(zone_id_t zone_id, void *addr __unsafe_indexable) { + free(addr); +} + +void zone_set_sig_eq(zone_t zone, zone_id_t sig_eq) { + zone_security_array[zone_index(zone)].z_sig_eq = sig_eq; +} + +zone_id_t zone_get_sig_eq(zone_t zone) { + return zone_security_array[zone_index(zone)].z_sig_eq; +} + +zone_t zone_by_id(size_t zid) { + if (zid == ZONE_ID_ANY) { + return NULL; + } + + if (zid >= MAX_ZONES) { + panic("zone_by_id: invalid zone id %d", zid); + } + + zone_t z = &zone_array[zid]; + + if (z->z_self != z) { + return NULL; + } + + return z; +} + +#undef zfree_nozero +void zfree_nozero(zone_id_t zone_id, void *elem __unsafe_indexable) { + (void)zone_id; // Zone information is not used in this fake implementation. + free(elem); +} diff --git a/fuzz/xnu/osfmk/test/osfmk_test_impl.c b/fuzz/xnu/osfmk/test/osfmk_test_impl.c new file mode 100644 index 0000000..6fa8d06 --- /dev/null +++ b/fuzz/xnu/osfmk/test/osfmk_test_impl.c @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include "fuzz/xnu/osfmk/test/osfmk_test_impl.h" + +#include +#include + +#include "fuzz/host/hypercall/hypercall.h" +#include "fuzz/xnu/common/test_utils.h" + +void test_kalloc_type_impl(TestResult* result) { + // Test kalloc_type(int, Z_WAITOK) + int* allocated = kalloc_type(int, Z_WAITOK); + XNU_ASSERT(result, allocated != NULL, "kalloc_type returned NULL"); + + if (allocated != NULL) { + // Perform some operations with the allocated memory + *allocated = 42; + XNU_ASSERT(result, *allocated == 42, + "Unexpected value in allocated memory"); + + // Free the allocated memory + kfree_type(int, allocated); + } + + if (is_verbose) { + printf("test_kalloc_type_impl completed with %d assertions, %d failures\n", + result->num_assertions, result->num_failures); + } +} diff --git a/fuzz/xnu/osfmk/test/osfmk_test_impl.h b/fuzz/xnu/osfmk/test/osfmk_test_impl.h new file mode 100644 index 0000000..c172cc9 --- /dev/null +++ b/fuzz/xnu/osfmk/test/osfmk_test_impl.h @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef FUZZ_XNU_OSFMK_TEST_OSFMK_TEST_IMPL_H_ +#define FUZZ_XNU_OSFMK_TEST_OSFMK_TEST_IMPL_H_ + +#include "fuzz/xnu/common/test_utils.h" + +/** + * @brief Test implementation for kalloc_type functionality. + * + * This function tests the basic allocation and deallocation using kalloc_type. + * It allocates memory for an integer, performs a simple operation, and then + * frees the memory. This test is meant to verify the basic functionality of + * kalloc_type and kfree_type within the XNU environment. + * + * @param result Pointer to a TestResult structure to store the test outcomes. + */ +void test_kalloc_type_impl(TestResult* result); + +#endif // FUZZ_XNU_OSFMK_TEST_OSFMK_TEST_IMPL_H_ \ No newline at end of file diff --git a/fuzz/xnu/san/BUILD.bazel b/fuzz/xnu/san/BUILD.bazel new file mode 100644 index 0000000..63335a8 --- /dev/null +++ b/fuzz/xnu/san/BUILD.bazel @@ -0,0 +1,10 @@ +load("//third_party/xnu:options.bzl", "SAN_DEFINES", "XNU_COMPILE_OPTIONS", "XNU_INCLUDES", "SAN_INCLUDES", "convert_includes_to_flags") + +cc_library( + name = "san", + srcs = ["fakes/san.c"], + copts = SAN_DEFINES + XNU_COMPILE_OPTIONS + convert_includes_to_flags(XNU_INCLUDES + SAN_INCLUDES), + linkstatic = 1, + visibility = ["//fuzz/xnu:__pkg__"], + deps = ["//third_party/xnu:san_internal"], +) diff --git a/fuzz/fakes/san.c b/fuzz/xnu/san/fakes/san.c similarity index 89% rename from fuzz/fakes/san.c rename to fuzz/xnu/san/fakes/san.c index aee5629..efc39db 100644 --- a/fuzz/fakes/san.c +++ b/fuzz/xnu/san/fakes/san.c @@ -26,20 +26,16 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -#include -#include - -#include #include +#include +#include -int printf(const char* format, ...); +int printf(const char *format, ...); void __asan_loadN(unsigned long addr, size_t size); void __asan_storeN(unsigned long addr, size_t size); -void NOINLINE -kasan_check_range(const void *x, size_t sz, access_t access) -{ +void NOINLINE kasan_check_range(const void *x, size_t sz, access_t access) { int read = access & TYPE_READ; int write = access & TYPE_WRITE; @@ -49,9 +45,9 @@ kasan_check_range(const void *x, size_t sz, access_t access) if (access & TYPE_WRITE) { __asan_storeN((unsigned long)x, sz); - } + } - if (!read && !write) { + if (!read && !write) { printf("Unhandled kasan access type %d\n", access); assert(false); } diff --git a/fuzz/xnu/test/BUILD.bazel b/fuzz/xnu/test/BUILD.bazel new file mode 100644 index 0000000..9c1954b --- /dev/null +++ b/fuzz/xnu/test/BUILD.bazel @@ -0,0 +1,23 @@ +load("//third_party/xnu:options.bzl", "OSFMK_DEFINES", "OSFMK_INCLUDES", "XNU_COMPILE_OPTIONS_NO_META", "XNU_INCLUDES", "convert_includes_to_flags") + +cc_library( + name = "headers", + hdrs = ["backend.h"], + visibility = ["//fuzz/host:__pkg__"], +) + +# cc_library( +# name = "backend", +# srcs = ["backend.cc"], +# copts = OSFMK_DEFINES + convert_includes_to_flags(XNU_INCLUDES + OSFMK_INCLUDES) + XNU_COMPILE_OPTIONS_NO_META, +# linkstatic = 1, +# visibility = [ +# "//fuzz/host:__pkg__", +# "//fuzz/xnu:__pkg__", +# ], +# deps = [ +# ":headers", +# "//third_party/xnu:osfmk_internal_headers", +# ], +# alwayslink = 1, +# ) diff --git a/fuzz/xnu/test/backend.cc b/fuzz/xnu/test/backend.cc new file mode 100644 index 0000000..f8bb34c --- /dev/null +++ b/fuzz/xnu/test/backend.cc @@ -0,0 +1,139 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +// Links against internal libxnu to make testing easier + +#include "fuzz/xnu/test/backend.h" + +#include + +#include +#include +#include +#include + +class Waitq::WaitqImpl { + public: + explicit WaitqImpl(sync_policy policy) { + waitq_.wq_q = &waitq_data_; + waitq_init(waitq_, WQT_QUEUE, static_cast(policy)); + } + ~WaitqImpl() { waitq_deinit(waitq_); } + WaitqImpl(const WaitqImpl &) = delete; + WaitqImpl &operator=(const WaitqImpl &) = delete; + WaitqImpl(WaitqImpl &&) = delete; + WaitqImpl &operator=(WaitqImpl &&) = delete; + + wait_result AssertWait64(event64_t wait_event, wait_interrupt interruptible, + uint64_t deadline) { + wait_result_t result = waitq_assert_wait64( + waitq_, wait_event, static_cast(interruptible), + deadline); + return static_cast(result); + } + + wait_result AssertWait64Leeway(event64_t wait_event, + wait_interrupt interruptible, + wait_timeout_urgency urgency, + uint64_t deadline, uint64_t leeway) { + wait_result_t result = waitq_assert_wait64_leeway( + waitq_, wait_event, static_cast(interruptible), + static_cast(urgency), deadline, leeway); + return static_cast(result); + } + + kern_return Wakeup64One(event64_t wake_event, wait_result result, + waitq_wakeup_flags_t flags) { + kern_return_t ret = waitq_wakeup64_one( + waitq_, wake_event, static_cast(result), flags); + return static_cast(ret); + } + + private: + struct waitq waitq_data_ {}; + waitq_t waitq_{}; +}; + +Waitq::Waitq(sync_policy policy) : impl_(new WaitqImpl(policy)) {} + +Waitq::~Waitq() { + delete impl_; +} + +// assert intent to wait on pair +wait_result Waitq::AssertWait64(event64_t wait_event, + wait_interrupt interruptible, + uint64_t deadline) { + return impl_->AssertWait64(wait_event, interruptible, deadline); +} + +wait_result Waitq::AssertWait64Leeway(event64_t wait_event, + wait_interrupt interruptible, + wait_timeout_urgency urgency, + uint64_t deadline, uint64_t leeway) { + return impl_->AssertWait64Leeway(wait_event, interruptible, urgency, deadline, + leeway); +} + +kern_return Waitq::Wakeup64One(event64_t wake_event, wait_result result, + wakeup_flags flags) { + return impl_->Wakeup64One(wake_event, result, static_cast(flags)); +} + +// kern_return_t Waitq::Wakeup64All(event64_t wake_event, wait_result_t result, +// int priority) { +// return waitq_wakeup64_all(&waitq_, wake_event, result, priority); +// } + +// /* wakeup a specified thread iff it's waiting on pair */ +// extern kern_return_t waitq_wakeup64_thread(struct waitq *waitq, +// event64_t wake_event, +// thread_t thread, +// wait_result_t result); + +// /* return a reference to the thread that was woken up */ +// extern thread_t waitq_wakeup64_identify(struct waitq *waitq, +// event64_t wake_event, +// wait_result_t result, +// int priority); +// thread_t Waitq::Wakeup64Identify(event64_t wake_event, wait_result_t result, +// int priority) { +// return waitq_wakeup64_identify(&waitq_, wake_event, result, priority); +// } + +// void Waitq::Lock() { waitq_lock(&waitq_); } + +// void Waitq::Unlock() { waitq_unlock(&waitq_); } + +void* KHeap::alloc(size_t size, int flags) { + return kheap_alloc(KHEAP_DEFAULT, size, static_cast(flags | Z_ZERO)); +} + +void KHeap::free_addr(void* ptr) { + kheap_free_addr(KHEAP_DEFAULT, ptr); +} diff --git a/fuzz/xnu/test/backend.h b/fuzz/xnu/test/backend.h new file mode 100644 index 0000000..92c7bd5 --- /dev/null +++ b/fuzz/xnu/test/backend.h @@ -0,0 +1,183 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef OSFMK_TEST_BACKEND_H_ +#define OSFMK_TEST_BACKEND_H_ + +#include +#include + +enum class sync_policy { + fifo = 0x0, + lifo = 0x2, + disable_irq = 0x8, +}; + +enum class wait_result { + waiting = -1, + awakened = 0, + timed_out = 1, + interrupted = 2, + restart = 3, + not_waiting = 10, +}; + +typedef unsigned long long uint64; + +typedef uint64 event64_t; +const event64_t no_event = 0; + +enum class wait_interrupt : uint32_t { + uninterruptible = 0x00000000, + interruptible = 0x00000001, + abortsafe = 0x00000002, + noreport_kernel = 0x80000000, + noreport_user = 0x40000000, + noreport = (noreport_kernel | noreport_user), +}; + +enum class wait_timeout_urgency { + sys_normal = 0x00, + sys_critical = 0x01, + sys_background = 0x02, + user_mask = 0x10, + user_normal = 0x10, + user_critical = 0x11, + user_background = 0x12, + mask = 0x13, + leeway = 0x20, + first_avail = 0x40, + ratelimited = 0x80 +}; + +enum class kern_return { + success = 0, + invalid_address = 1, + protection_failure = 2, + no_space = 3, + invalid_argument = 4, + failure = 5, + resource_shortage = 6, + not_receiver = 7, + no_access = 8, + memory_failure = 9, + memory_error = 10, + already_in_set = 11, + not_in_set = 12, + name_exists = 13, + aborted = 14, + invalid_name = 15, + invalid_task = 16, + invalid_right = 17, + invalid_value = 18, + urefs_overflow = 19, + invalid_capability = 20, + right_exists = 21, + invalid_host = 22, + memory_present = 23, + memory_data_moved = 24, + memory_restart_copy = 25, + invalid_processor_set = 26, + policy_limit = 27, + invalid_policy = 28, + invalid_object = 29, + already_waiting = 30, + default_set = 31, + exception_protected = 32, + invalid_ledger = 33, + invalid_memory_control = 34, + invalid_security = 35, + not_depressed = 36, + terminated = 37, + lock_set_destroyed = 38, + lock_unstable = 39, + lock_owned = 40, + lock_owned_self = 41, + semaphore_destroyed = 42, + rpc_server_terminated = 43, + rpc_terminate_orphan = 44, + rpc_continue_orphan = 45, + not_supported = 46, + node_down = 47, + not_waiting = 48, + operation_timed_out = 49, + codesign_error = 50, + policy_static = 51, + insufficient_buffer_size = 52, + denied = 53, + missing_kc = 54, + invalid_kc = 55, + not_found = 56, +}; + +enum class wakeup_flags { + WAITQ_WAKEUP_DEFAULT = 0x0000, + WAITQ_UPDATE_INHERITOR = 0x0001, + WAITQ_PROMOTE_PRIORITY = 0x0002, + WAITQ_UNLOCK = 0x0004, + WAITQ_KEEP_LOCKED = 0x0000, + WAITQ_HANDOFF = 0x0008, +}; + +class Waitq { + public: + explicit Waitq(sync_policy policy); + ~Waitq(); + Waitq(const Waitq &) = delete; + Waitq &operator=(const Waitq &) = delete; + Waitq(Waitq &&) = delete; + Waitq &operator=(Waitq &&) = delete; + + wait_result AssertWait64(event64_t wait_event, wait_interrupt interruptible, + uint64 deadline); + + wait_result AssertWait64Leeway(event64_t wait_event, + wait_interrupt interruptible, + wait_timeout_urgency urgency, uint64 deadline, + uint64 leeway); + + kern_return Wakeup64One(event64_t wake_event, wait_result result, + wakeup_flags flags); + + private: + class WaitqImpl; + + WaitqImpl* impl_; +}; + +class KHeap { +public: + KHeap() = default; + ~KHeap() = default; + + void* alloc(size_t size, int flags); + + void free_addr(void* ptr); +}; + +#endif /* OSFMK_TEST_BACKEND_H_ */ diff --git a/fuzz/xnu/xnu_version_script.lds b/fuzz/xnu/xnu_version_script.lds new file mode 100644 index 0000000..d7b0ded --- /dev/null +++ b/fuzz/xnu/xnu_version_script.lds @@ -0,0 +1,43 @@ +{ + global: + *_wrapper; + GetThreadState; + CloneTaskFromInit; + GetTaskFromThread; + CreateWaitingThread; + _Z*Waitq*; + _Z*KHeap*; + ClearTaskWait; + thread_deallocate; + XNUReapInitChildren; + nprocs; + CheckSchedulerState; + thread_set_thread_name; + XNUGetThreadInfo; + XNUThreadTerminate; + ast_taken_user; + XNUThreadEnqueueTermination; + XNUThreadSchedCallNotifyBlocked; + initialize_kernel; + XNUCurrentProc; + XNUThreadMarkWaitLocked; + XNUThreadWait; + XNUThreadSignalWake; + XNUThreadCancelWaitTimer; + XNUThreadSchedCallNotifyUnblocked; + XNUThreadAbort; + KillAllNonInitProcs; + XNUDoRandomSyscall; + DoRandomMachTrap; + ioctls; + siocaifaddr_in6_64; + siocsifflags; + DoVmFault; + get_mbuf_data; + # TODO(nedwill): figure out if these are needed for clang coverage build + *.__covrec_*; + *llvm*; + *.__profc_*; + local: + *; +}; diff --git a/llvm_toolchain.nix b/llvm_toolchain.nix new file mode 100644 index 0000000..4c18f43 --- /dev/null +++ b/llvm_toolchain.nix @@ -0,0 +1,30 @@ +with import { + config = {}; + overlays = []; +}; let + pkgs = import {}; + llvm = pkgs.llvmPackages_18; + lld = pkgs.lld; + mold = pkgs.mold; + libcxx = llvm.libcxx; + compiler-rt = llvm.compiler-rt; + stdenv = llvm.stdenv; + + clang = llvm.libstdcxxClang; +in + pkgs.buildEnv { + name = "llvm-cc-wrapper"; + paths = [ + clang + lld + mold + ]; + pathsToLink = ["/bin"]; # Consider reviewing this + passthru = { + isClang = true; + inherit (stdenv.cc) targetPrefix; + # Review and remove potentially unnecessary attributes + inherit (clang) libc nativePlugins nativeLibc nativeTools; + inherit (clang) gcc lib version; + }; + } diff --git a/nixpkgs.nix b/nixpkgs.nix new file mode 100644 index 0000000..375f4f6 --- /dev/null +++ b/nixpkgs.nix @@ -0,0 +1,6 @@ +let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + spec = lock.nodes.nixpkgs.locked; + nixpkgs = fetchTarball "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; +in +import nixpkgs diff --git a/sockfuzzer_base_image.nix b/sockfuzzer_base_image.nix new file mode 100644 index 0000000..24adcd2 --- /dev/null +++ b/sockfuzzer_base_image.nix @@ -0,0 +1,37 @@ +with import {}; + +let + basePackages = [ + glibc + stdenv.cc.cc.lib + libunwind + xz.out + elfutils.out + openssl.out + # llvm-symbolizer + clang_18 + llvmPackages_18.stdenv + lld_18 + # For computing pod index + gawk + python3 + ]; + # TODO(nedwill): consider disabling these for production images + debugPackages = [ + coreutils + bashInteractive + binutils + gnugrep + # lddtree + pax-utils + gdb + ]; + dockerImage = dockerTools.buildLayeredImage { + name = "fuzzer-base-image"; + created = "now"; + contents = basePackages ++ debugPackages; + }; +in runCommand "sockfuzzer-base-image" { } '' + mkdir -p $out/bin + gzip -dc ${dockerImage} > $out/image.tar +'' diff --git a/third_party/concurrence/.bazelrc b/third_party/concurrence/.bazelrc deleted file mode 100644 index 71d7b1e..0000000 --- a/third_party/concurrence/.bazelrc +++ /dev/null @@ -1,13 +0,0 @@ -build --action_env=CC=clang -build --action_env=CXX=clang++ -build --action_env=BAZEL_CXXOPTS="-std=c++20" - -build:asan --copt=-fsanitize=address -build:asan --linkopt=-fsanitize=address -# Support fast unwind -build:asan --copt=-fno-omit-frame-pointer - -# TODO(nedwill): Investigate bugs in ASAN fiber support when used with libco; -# We should only enable these options when selecting the libco backend. -build:asan --copt=-mllvm -build:asan --copt=-asan-stack=0 diff --git a/third_party/concurrence/LICENSE b/third_party/concurrence/LICENSE deleted file mode 100644 index 7a4a3ea..0000000 --- a/third_party/concurrence/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/third_party/concurrence/README.md b/third_party/concurrence/README.md deleted file mode 100644 index 9accab3..0000000 --- a/third_party/concurrence/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Concurrence - -Concurrence is a library for fuzzing multi-threaded targets. It schedules one -thread at a time cooperatively and selects threads using fuzzed input data. This -project is still in early stages and this repository serves as a reference. - -There are three main components. The executor handles how contexts (threads) get -created, deleted, and defines how to switch between them. The scheduler depends -on the abstract executor interface. We provide a fuzzed scheduler implementation -to expose the scheduling decisions to fuzzed inputs. The sync primitives depend -on the scheduler and provide an example of how to integrate with the scheduler. - -In order to support developers' goals, every component is interchangeable. You -could have your own notion of thread creation and switching and write a custom -executor implementation. You could also opt for another scheduler implementation. -Finally, the sync primitives might not match the behavior of your codebase. You -could use your existing sync primitives without an issue so long as you can -Block() or Yield() appropriately so the scheduler knows which contexts are runnable. - -# Kernel vs. Userspace - -Currently, the project targets a kernel which means it has to handle things -like interrupt masking and deleting a thread that's waiting to return from a -context switch. These aspects are incompatible with userspace target fuzzing. -A future version of this library will attempt to bridge this gap so it can be -used for arbitrary targets. Until then, do not expect the API to remain stable. - -# Documentation - -See presentations/catch_me_if_you_can.pdf to read the slides from my Black Hat 2022 -presentation on this project. It covers the design and motivation and provides some -real world examples of bugs found using this project. - -# Build - -This project is built using Bazel. The //scheduler:fuzzed_scheduler target, when paired -with a suitable executor for your codebase, is the core class of this project and is -probably what you're looking for if you're not using this as a generic execution library. - -You can use or review //sync:mutex and //sync:rwlock to see how to interact with the -scheduler from your target code to explicitly yield or block in code that was designed -for multi-threaded environments. diff --git a/third_party/concurrence/WORKSPACE.bazel b/third_party/concurrence/WORKSPACE.bazel deleted file mode 100644 index bc6c85b..0000000 --- a/third_party/concurrence/WORKSPACE.bazel +++ /dev/null @@ -1,24 +0,0 @@ -workspace(name = "concurrence") - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "bazel_skylib", - sha256 = "20ee473b16cc22958a408e83e816a352857e825b39f9d3666a99e058edc53623", - strip_prefix = "bazel-skylib-bc112d41fd71a5abf51ff827f19a6af9ed81af43", - urls = ["https://github.com/bazelbuild/bazel-skylib/archive/bc112d41fd71a5abf51ff827f19a6af9ed81af43.zip"], -) - -http_archive( - name = "com_google_absl", - sha256 = "e43a13c52747d056ac565f208087fca79dc294cf90795382be1217e22b80c076", - strip_prefix = "abseil-cpp-8317b9a01cbc32594ad4bf971709c97cb13ec921", - urls = ["https://github.com/abseil/abseil-cpp/archive/8317b9a01cbc32594ad4bf971709c97cb13ec921.zip"], -) - -http_archive( - name = "com_google_googletest", - sha256 = "bb38e3f07350fa48c49309d4e26dd19c00f7f76dde65d0b5bda75af3234f0e20", - strip_prefix = "googletest-67e264834a45c3d543aa2fa11bcc41c4ba90316b", - urls = ["https://github.com/google/googletest/archive/67e264834a45c3d543aa2fa11bcc41c4ba90316b.zip"], -) diff --git a/third_party/concurrence/backtrace/BUILD.bazel b/third_party/concurrence/backtrace/BUILD.bazel index cb238b5..68bbffa 100644 --- a/third_party/concurrence/backtrace/BUILD.bazel +++ b/third_party/concurrence/backtrace/BUILD.bazel @@ -1,35 +1,26 @@ -# Copyright 2022 Google LLC -# -# 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 -# -# https://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. +# TODO(nedwill): move this library to fuzz/host cc_library( name = "backtrace", - srcs = [ - "backtrace.cc", - ], + srcs = select({ + "//:asan_enabled": ["backtrace_null.cc"], + "//conditions:default": ["backtrace_backward.cc"], + }), hdrs = [ "backtrace.h", ], visibility = [ - "//fuzz/host:__pkg__", - "//fuzz/host/hypercall:__pkg__", - "//:__subpackages__", + "//third_party/concurrence:__subpackages__", ], deps = [ - "@com_google_absl//absl/debugging:stacktrace", - "@com_google_absl//absl/debugging:symbolize", "@com_google_absl//absl/log", - ], + ] + select({ + "//:asan_enabled": [ + "@com_google_absl//absl/debugging:stacktrace", + "@com_google_absl//absl/debugging:symbolize", + ], + "//conditions:default": ["//third_party/backward-cpp:backward"], + }), ) cc_test( @@ -39,7 +30,7 @@ cc_test( "backtrace_test.cc", ], deps = [ - "//backtrace", + "//third_party/concurrence/backtrace", "@com_google_googletest//:gtest_main", ], ) diff --git a/third_party/concurrence/backtrace/backtrace.h b/third_party/concurrence/backtrace/backtrace.h index 1d8144d..96e782c 100644 --- a/third_party/concurrence/backtrace/backtrace.h +++ b/third_party/concurrence/backtrace/backtrace.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,20 @@ #ifndef CONCURRENCE_BACKTRACE_H_ #define CONCURRENCE_BACKTRACE_H_ -#include +#include -void GetBacktrace(std::vector &stack); -void PrintBacktraceFromStack(const std::vector &stack); +class StackTraceImpl; // Forward declare the implementation class -#endif +class StackTrace { +public: + StackTrace(); + ~StackTrace(); + + void Print() const; + bool Empty() const; + +private: + std::unique_ptr impl_; +}; + +#endif // CONCURRENCE_BACKTRACE_H_ diff --git a/third_party/concurrence/backtrace/backtrace_backward.cc b/third_party/concurrence/backtrace/backtrace_backward.cc new file mode 100644 index 0000000..2754d19 --- /dev/null +++ b/third_party/concurrence/backtrace/backtrace_backward.cc @@ -0,0 +1,37 @@ +// Copyright 2024 Google LLC +// +// 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 "backtrace.h" +#include "third_party/backward-cpp/backward.hpp" + +class StackTraceImpl { +public: + backward::StackTrace stacktrace; +}; + +StackTrace::StackTrace() : impl_(new StackTraceImpl) { + impl_->stacktrace.load_here(32); +} + +StackTrace::~StackTrace() = default; + +void StackTrace::Print() const { + backward::Printer printer; + printer.color_mode = backward::ColorMode::type::always; + printer.print(impl_->stacktrace); +} + +bool StackTrace::Empty() const { + return impl_->stacktrace.size() == 0; +} diff --git a/third_party/concurrence/backtrace/backtrace.cc b/third_party/concurrence/backtrace/backtrace_null.cc similarity index 56% rename from third_party/concurrence/backtrace/backtrace.cc rename to third_party/concurrence/backtrace/backtrace_null.cc index e5fba48..45b4f4a 100644 --- a/third_party/concurrence/backtrace/backtrace.cc +++ b/third_party/concurrence/backtrace/backtrace_null.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,24 +19,30 @@ #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" -#include "absl/log/log.h" -#include "absl/strings/str_format.h" +class StackTraceImpl { +public: + std::vector stacktrace; +}; -void GetBacktrace(std::vector &stack) { - stack.clear(); - std::array result{}; - std::array sizes{}; - int depth = - absl::GetStackFrames(result.data(), sizes.data(), result.size(), 1); - stack = std::vector(result.data(), result.data() + depth); +StackTrace::StackTrace() : impl_(new StackTraceImpl) { + std::array result{}; + std::array sizes{}; + int depth = + absl::GetStackFrames(result.data(), sizes.data(), result.size(), 1); + impl_->stacktrace = std::vector(result.data(), result.data() + depth); } -void PrintBacktraceFromStack(const std::vector &stack) { - for (const void *pc : stack) { +StackTrace::~StackTrace() = default; + +void StackTrace::Print() const { + for (const void *pc : impl_->stacktrace) { std::array symbol{}; absl::Symbolize(const_cast(pc), symbol.data(), symbol.size()); - - LOG(INFO) << absl::StrFormat("pc=%p %s\n", pc, symbol.data()); + printf("pc=%p %s\n", pc, symbol.data()); } } + +bool StackTrace::Empty() const { + return impl_->stacktrace.empty(); +} diff --git a/third_party/concurrence/backtrace/backtrace_test.cc b/third_party/concurrence/backtrace/backtrace_test.cc index 229e186..099d992 100644 --- a/third_party/concurrence/backtrace/backtrace_test.cc +++ b/third_party/concurrence/backtrace/backtrace_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "backtrace/backtrace.h" +#include "third_party/concurrence/backtrace/backtrace.h" #include @@ -22,8 +22,7 @@ TEST(BacktraceTest, PopulatesStackAndDoesntCrash) { #ifdef NDEBUG GTEST_SKIP(); #endif - std::vector stack; - GetBacktrace(stack); - EXPECT_FALSE(stack.empty()); - PrintBacktraceFromStack(stack); + auto stacktrace = std::make_unique(); + EXPECT_FALSE(stacktrace->Empty()); + stacktrace->Print(); } diff --git a/third_party/concurrence/executor/BUILD.bazel b/third_party/concurrence/executor/BUILD.bazel index 4f45c33..0e3dab6 100644 --- a/third_party/concurrence/executor/BUILD.bazel +++ b/third_party/concurrence/executor/BUILD.bazel @@ -1,17 +1,3 @@ -# Copyright 2022 Google LLC -# -# 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 -# -# https://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. - cc_library( name = "headers", hdrs = [ @@ -30,7 +16,7 @@ cc_library( ], deps = [ ":headers", - "//backtrace", + "//third_party/concurrence/backtrace", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/log:check", ], diff --git a/third_party/concurrence/executor/executor.cc b/third_party/concurrence/executor/executor.cc index 6bb639b..050fd28 100644 --- a/third_party/concurrence/executor/executor.cc +++ b/third_party/concurrence/executor/executor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,14 +14,14 @@ #include "executor.h" -#include "backtrace/backtrace.h" +#include "third_party/concurrence/backtrace/backtrace.h" ThreadHandle g_current_thread; Executor::~Executor() = default; void Executor::SetBacktrace(ThreadHandle handle) { - GetBacktrace(backtraces_[handle]); + backtraces_[handle] = std::make_unique(); } void Executor::PrintBacktrace(ThreadHandle handle) { @@ -29,7 +29,7 @@ void Executor::PrintBacktrace(ThreadHandle handle) { return; } - PrintBacktraceFromStack(backtraces_[handle]); + backtraces_[handle]->Print(); } void Executor::DeleteBacktrace(ThreadHandle handle) { diff --git a/third_party/concurrence/executor/executor.h b/third_party/concurrence/executor/executor.h index 93d7124..71547f2 100644 --- a/third_party/concurrence/executor/executor.h +++ b/third_party/concurrence/executor/executor.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,17 @@ #define EXECUTOR_H_ #include +// Hides implementation details about specific context-switching primitive +// (currently coroutines and synchronized threads) +#include #include +#include #include #include #include "absl/container/flat_hash_map.h" #include "absl/hash/hash.h" +#include "third_party/concurrence/backtrace/backtrace.h" // Use pointer type as we use pointers the the corresponding C++ objects // as handles @@ -69,7 +74,7 @@ class Executor { private: Executor::CallbackInterface *callbacks_{}; - absl::flat_hash_map> backtraces_; + absl::flat_hash_map> backtraces_; }; #endif /* EXECUTOR_H_ */ diff --git a/third_party/concurrence/executor/executor_test.cc b/third_party/concurrence/executor/executor_test.cc index c494b61..1b50c56 100644 --- a/third_party/concurrence/executor/executor_test.cc +++ b/third_party/concurrence/executor/executor_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" +#include + +#include "third_party/concurrence/executor/executor_test_template.h" +#include "third_party/concurrence/executor/stdthread/thread_executor.h" #include "gtest/gtest.h" -#include "executor/executor_test_template.h" -#include "executor/stdthread/thread_executor.h" +bool is_verbose = false; Executor* g_executor; diff --git a/third_party/concurrence/executor/executor_test_template.h b/third_party/concurrence/executor/executor_test_template.h index 43a17e0..311204f 100644 --- a/third_party/concurrence/executor/executor_test_template.h +++ b/third_party/concurrence/executor/executor_test_template.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ #include #include -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" class EmptyExecutorCallbacks : public Executor::CallbackInterface { void ThreadDestroyed(ThreadHandle handle) override {} diff --git a/third_party/concurrence/executor/stdthread/thread.cc b/third_party/concurrence/executor/stdthread/thread.cc index 1c4e9e3..54ef22e 100644 --- a/third_party/concurrence/executor/stdthread/thread.cc +++ b/third_party/concurrence/executor/stdthread/thread.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,14 +14,14 @@ #include "thread.h" -#include #include +#include #include #include "absl/log/check.h" -#include "thread_executor.h" #include "absl/synchronization/mutex.h" -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" +#include "thread_executor.h" void CooperativeThread::Start() { tid_ = gettid(); diff --git a/third_party/concurrence/executor/stdthread/thread.h b/third_party/concurrence/executor/stdthread/thread.h index e73353b..7383811 100644 --- a/third_party/concurrence/executor/stdthread/thread.h +++ b/third_party/concurrence/executor/stdthread/thread.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,14 @@ #include #include +#include #include #include -#include +#include "absl/synchronization/mutex.h" #include "absl/base/thread_annotations.h" class ThreadExecutor; - namespace absl { class Mutex; } // namespace absl diff --git a/third_party/concurrence/executor/stdthread/thread_executor.cc b/third_party/concurrence/executor/stdthread/thread_executor.cc index fcd4ab9..ac5ce83 100644 --- a/third_party/concurrence/executor/stdthread/thread_executor.cc +++ b/third_party/concurrence/executor/stdthread/thread_executor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,9 +18,9 @@ #include "absl/log/check.h" #include "absl/strings/str_format.h" -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" #include "absl/hash/hash.h" -#include "executor/stdthread/thread.h" +#include "third_party/concurrence/executor/stdthread/thread.h" ThreadExecutor::ThreadExecutor() { g_current_thread = MAIN_THREAD_HANDLE; diff --git a/third_party/concurrence/executor/stdthread/thread_executor.h b/third_party/concurrence/executor/stdthread/thread_executor.h index 8fe24f9..591081d 100644 --- a/third_party/concurrence/executor/stdthread/thread_executor.h +++ b/third_party/concurrence/executor/stdthread/thread_executor.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" - -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" +#include "thread.h" class CooperativeThread; @@ -32,6 +32,10 @@ class ThreadExecutor : public Executor { public: ThreadExecutor(); ~ThreadExecutor() override; + ThreadExecutor(const ThreadExecutor &) = delete; + ThreadExecutor(ThreadExecutor &&) = delete; + ThreadExecutor &operator=(const ThreadExecutor &) = delete; + ThreadExecutor &operator=(ThreadExecutor &&) = delete; ThreadHandle CreateThread(std::function target) override; void DeleteThread(ThreadHandle handle) override; diff --git a/third_party/concurrence/scheduler/BUILD.bazel b/third_party/concurrence/scheduler/BUILD.bazel index 1a669fd..b3c297d 100644 --- a/third_party/concurrence/scheduler/BUILD.bazel +++ b/third_party/concurrence/scheduler/BUILD.bazel @@ -1,17 +1,3 @@ -# Copyright 2022 Google LLC -# -# 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 -# -# https://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. - cc_library( name = "headers", hdrs = [ @@ -19,7 +5,10 @@ cc_library( "scheduler.h", ], visibility = ["//visibility:public"], - deps = ["//executor:headers"], + deps = [ + "@compiler_rt_dev//:compiler-rt-headers", + "//third_party/concurrence/executor:headers", + ], ) cc_library( @@ -32,7 +21,7 @@ cc_library( ], deps = [ ":headers", - "//executor", + "//third_party/concurrence/executor", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings:str_format", ], @@ -61,7 +50,7 @@ cc_test( deps = [ ":fuzzed_scheduler", ":fuzzed_scheduler_test_template", - "//executor:stdthread_executor", + "//third_party/concurrence/executor:stdthread_executor", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], diff --git a/third_party/concurrence/scheduler/fuzzed_scheduler.cc b/third_party/concurrence/scheduler/fuzzed_scheduler.cc index 861098c..48896ed 100644 --- a/third_party/concurrence/scheduler/fuzzed_scheduler.cc +++ b/third_party/concurrence/scheduler/fuzzed_scheduler.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,15 +20,13 @@ #include #include -#include "absl/log/log.h" #include "absl/strings/str_format.h" -#include "executor/executor.h" -#include "scheduler/scheduler.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/scheduler.h" FuzzedScheduler::FuzzedScheduler( Executor *executor, Scheduler::CallbackInterface *callback_interface) : executor_(executor), - enable_debug_checks_(false), callbacks_(callback_interface), thread_choices_(nullptr), interrupts_enabled_(true), @@ -87,14 +85,19 @@ void FuzzedScheduler::Yield() { // Yielding in main thread is a nop if (g_current_thread == executor_->GetMainThreadHandle()) { + // printf("FuzzedScheduler::Yield: in main thread\n"); return; } + // don't yield if interrupts are enabled if (!interrupts_enabled_) { + // printf("FuzzedScheduler::Yield: interrupts disabled\n"); return; } + // Don't yield if we will immediately schedule this thread again if (runnable_.empty()) { + // printf("FuzzedScheduler::Yield: nothing available to run\n"); return; } @@ -103,11 +106,14 @@ void FuzzedScheduler::Yield() { } void FuzzedScheduler::Block() { + // printf("FuzzedScheduler::Block: blocking in thread %p\n", (void*)handle); if (g_current_thread == executor_->GetMainThreadHandle()) { std::cout << "FuzzedScheduler::Block: blocking main thread is not allowed\n"; abort(); } + // printf("Block removing runnable 0x%lx\n", handle); + // runnable_set_.erase(handle); ThreadHandle main_handle = executor_->GetMainThreadHandle(); callbacks_->ThreadWillBlock(); SwitchTo(main_handle); @@ -158,9 +164,6 @@ void FuzzedScheduler::SwitchTo(ThreadHandle handle) { return; } - if (enable_debug_checks_) { - executor_->SetBacktrace(g_current_thread); - } executor_->SwitchTo(handle); callbacks_->ThreadResumed(); } @@ -194,11 +197,13 @@ void FuzzedScheduler::MakeRunnable(ThreadHandle handle) { #ifndef NDEBUG if (!handle) { - LOG(FATAL) << "missing handle\n"; + std::cout << "missing handle\n"; + abort(); } if (pending_deletion_.contains(handle)) { - LOG(FATAL) << "pending deletion\n"; + std::cout << "pending deletion\n"; + abort(); } #endif @@ -222,7 +227,7 @@ void FuzzedScheduler::MakeNotRunnable(ThreadHandle handle) { } void FuzzedScheduler::MakeAllRunnable( - const absl::flat_hash_set &runnable) { + const std::vector &runnable) { for (const ThreadHandle &handle : runnable) { MakeRunnable(handle); } @@ -284,28 +289,28 @@ ThreadHandle FuzzedScheduler::ChooseThread(bool can_choose_existing) { } void FuzzedScheduler::PrintThreadState(ThreadHandle handle) { - LOG(INFO) << "; "; + std::cout << "; "; bool printed = false; if (IsRunnable(handle)) { - LOG(INFO) << "RUNNABLE"; + std::cout << "RUNNABLE"; printed = true; } if (live_set_.contains(handle)) { if (printed) { - LOG(INFO) << ", "; + std::cout << ", "; } - LOG(INFO) << "ALIVE"; + std::cout << "ALIVE"; printed = true; } if (pending_deletion_.contains(handle)) { if (printed) { - LOG(INFO) << ", "; + std::cout << ", "; } - LOG(INFO) << "PENDING DELETION"; + std::cout << "PENDING DELETION"; printed = true; } if (printed) { - LOG(INFO) << "\n"; + std::cout << "\n"; } } diff --git a/third_party/concurrence/scheduler/fuzzed_scheduler.h b/third_party/concurrence/scheduler/fuzzed_scheduler.h index 1842274..a704012 100644 --- a/third_party/concurrence/scheduler/fuzzed_scheduler.h +++ b/third_party/concurrence/scheduler/fuzzed_scheduler.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,23 @@ #ifndef SCHEDULER_IMPL_H_ #define SCHEDULER_IMPL_H_ +#include #include #include #include -#include +#include #include +#include #include +#include +#include #include #include +#include "third_party/concurrence/executor/executor.h" +#include "scheduler.h" #include "absl/container/flat_hash_set.h" -#include "executor/executor.h" -#include "scheduler/scheduler.h" - class FuzzedDataProvider; class FuzzedScheduler : public Scheduler { @@ -38,6 +41,10 @@ class FuzzedScheduler : public Scheduler { FuzzedScheduler(Executor *executor, Scheduler::CallbackInterface *callback_interface); ~FuzzedScheduler() override = default; + FuzzedScheduler(const FuzzedScheduler &) = delete; + FuzzedScheduler &operator=(const FuzzedScheduler &) = delete; + FuzzedScheduler(const FuzzedScheduler &&) = delete; + FuzzedScheduler &operator=(FuzzedScheduler &&) = delete; // External scheduler support ThreadHandle CreateThread(std::function target, @@ -58,7 +65,7 @@ class FuzzedScheduler : public Scheduler { void MakeRunnable(ThreadHandle handle) override; void MakeNotRunnable(ThreadHandle handle) override; void MakeAllRunnable( - const absl::flat_hash_set &runnables) override; + const std::vector &runnables) override; bool IsRunnable(ThreadHandle handle) override; void CleanupDeadThreads() override; @@ -70,7 +77,7 @@ class FuzzedScheduler : public Scheduler { // Util void SetRandSeed(uint32_t rand_seed) override; - void SetThreadChoices(FuzzedDataProvider *thread_choices) override; + void SetThreadChoices(FuzzedDataProvider* thread_choices) override; ThreadHandle GetMainThread() override; ThreadHandle GetCurrentThreadHandle() override; const std::set &GetLiveThreads() override { return live_set_; } @@ -89,7 +96,6 @@ class FuzzedScheduler : public Scheduler { private: std::unique_ptr executor_; - bool enable_debug_checks_; Scheduler::CallbackInterface *callbacks_; diff --git a/third_party/concurrence/scheduler/fuzzed_scheduler_test.cc b/third_party/concurrence/scheduler/fuzzed_scheduler_test.cc index 484f55b..bbf8568 100644 --- a/third_party/concurrence/scheduler/fuzzed_scheduler_test.cc +++ b/third_party/concurrence/scheduler/fuzzed_scheduler_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,10 +14,13 @@ #include "fuzzed_scheduler.h" +#include "third_party/concurrence/executor/stdthread/thread_executor.h" #include "fuzzed_scheduler_test_template.h" #include "gtest/gtest.h" -#include "executor/stdthread/thread_executor.h" +class Scheduler; + +bool is_verbose = false; Scheduler* g_scheduler; diff --git a/third_party/concurrence/scheduler/fuzzed_scheduler_test_template.h b/third_party/concurrence/scheduler/fuzzed_scheduler_test_template.h index 691743a..30e0ca8 100644 --- a/third_party/concurrence/scheduler/fuzzed_scheduler_test_template.h +++ b/third_party/concurrence/scheduler/fuzzed_scheduler_test_template.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ #include #include -#include "scheduler/fuzzed_scheduler.h" +#include "third_party/concurrence/scheduler/fuzzed_scheduler.h" extern Scheduler *g_scheduler; diff --git a/third_party/concurrence/scheduler/mock_scheduler.h b/third_party/concurrence/scheduler/mock_scheduler.h index 4dc9035..fa35520 100644 --- a/third_party/concurrence/scheduler/mock_scheduler.h +++ b/third_party/concurrence/scheduler/mock_scheduler.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ class MockScheduler : public Scheduler { MOCK_METHOD(void, MakeRunnable, (ThreadHandle id), (override)); MOCK_METHOD(void, MakeNotRunnable, (ThreadHandle id), (override)); MOCK_METHOD(void, MakeAllRunnable, - (const absl::flat_hash_set &runnable), (override)); + (const std::vector &runnable), (override)); MOCK_METHOD(bool, IsRunnable, (ThreadHandle handle), (override)); MOCK_METHOD(void, CleanupDeadThreads, (), (override)); diff --git a/third_party/concurrence/scheduler/scheduler.h b/third_party/concurrence/scheduler/scheduler.h index ee96f5e..742979c 100644 --- a/third_party/concurrence/scheduler/scheduler.h +++ b/third_party/concurrence/scheduler/scheduler.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,7 @@ #include #include -#include "absl/container/flat_hash_set.h" -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" class Scheduler : public Executor::CallbackInterface { public: @@ -49,7 +48,7 @@ class Scheduler : public Executor::CallbackInterface { virtual void MakeRunnable(ThreadHandle id) = 0; virtual void MakeNotRunnable(ThreadHandle id) = 0; virtual void MakeAllRunnable( - const absl::flat_hash_set &runnable) = 0; + const std::vector &runnable) = 0; virtual bool IsRunnable(ThreadHandle handle) = 0; virtual void CleanupDeadThreads() = 0; @@ -61,7 +60,7 @@ class Scheduler : public Executor::CallbackInterface { // Util virtual void SetRandSeed(uint32_t rand_seed) = 0; - virtual void SetThreadChoices(FuzzedDataProvider *thread_choices) = 0; + virtual void SetThreadChoices(FuzzedDataProvider* thread_choices) = 0; virtual ThreadHandle GetMainThread() = 0; virtual ThreadHandle GetCurrentThreadHandle() = 0; virtual const std::set &GetLiveThreads() = 0; diff --git a/third_party/concurrence/sync/BUILD.bazel b/third_party/concurrence/sync/BUILD.bazel index da36496..a30e4fe 100644 --- a/third_party/concurrence/sync/BUILD.bazel +++ b/third_party/concurrence/sync/BUILD.bazel @@ -1,17 +1,3 @@ -# Copyright 2022 Google LLC -# -# 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 -# -# https://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. - cc_library( name = "sync", srcs = [ @@ -33,8 +19,8 @@ cc_library( "//fuzz/xnu/osfmk:__pkg__", ], deps = [ - "//executor", - "//scheduler:headers", + "//third_party/concurrence/executor", + "//third_party/concurrence/scheduler:headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", ], @@ -49,7 +35,7 @@ cc_test( ], deps = [ ":sync", - "//scheduler:mock_scheduler", + "//third_party/concurrence/scheduler:mock_scheduler", "@com_google_googletest//:gtest_main", ], ) diff --git a/third_party/concurrence/sync/mutex.cc b/third_party/concurrence/sync/mutex.cc index 0081a0f..08dea8c 100644 --- a/third_party/concurrence/sync/mutex.cc +++ b/third_party/concurrence/sync/mutex.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,12 +16,14 @@ #include -#include "backtrace/backtrace.h" -#include "executor/executor.h" +#include "third_party/concurrence/backtrace/backtrace.h" +#include "third_party/concurrence/executor/executor.h" #include "absl/container/flat_hash_map.h" #include "absl/hash/hash.h" -#include "scheduler/scheduler.h" -#include "sync/sync.h" +#include "third_party/concurrence/scheduler/scheduler.h" +#include "third_party/concurrence/sync/sync.h" + +extern bool is_verbose; VirtualMutex::VirtualMutex(SyncTracker *sync_tracker) : Sync(sync_tracker), owner_(0) {} @@ -31,13 +33,18 @@ VirtualMutex::~VirtualMutex() = default; void VirtualMutex::Lock() { while (owner_) { AddWaiter(); + // printf("VirtualMutex::Lock: blocking; have owner at &owner_ %p for this %p\n", &owner_, this); + // printf("VirtualMutex::Lock: waiters size now %zu for this %p\n", waiters().size(), this); scheduler()->Block(); + // printf("VirtualMutex::Lock: woke up, going to check &owner_ %p for this %p\n", &owner_, this); } + RemoveWaiter(); + // printf("VirtualMutex::Lock(%p) setting handle %lx\n", this, handle); owner_ = g_current_thread; - if (enable_debug_checks_) { - GetBacktrace(owner_backtraces()[owner_]); + if (is_verbose) { + owner_backtraces()[owner_] = std::make_unique(); } } @@ -47,13 +54,14 @@ bool VirtualMutex::TryLock() { HandleNestedWait(); } if (owner_) { + // printf("VirtualMutex::TryLock(%p) failed to acquire the lock\n", this); return false; } owner_ = g_current_thread; - if (enable_debug_checks_) { - GetBacktrace(owner_backtraces()[owner_]); + if (is_verbose) { + owner_backtraces()[owner_] = std::make_unique(); } return true; @@ -61,18 +69,18 @@ bool VirtualMutex::TryLock() { void VirtualMutex::Unlock() { AssertHeld(); - if (enable_debug_checks_) { + if (is_verbose) { owner_backtraces().erase(owner_); } owner_ = 0; - ClearAndUnblockWaiters(); + WakeupWaiters(); } -bool VirtualMutex::Held() { +bool VirtualMutex::Held() const { return owner_ == g_current_thread; } -void VirtualMutex::AssertHeld() { +void VirtualMutex::AssertHeld() const { if (!Held()) { abort(); } diff --git a/third_party/concurrence/sync/mutex.h b/third_party/concurrence/sync/mutex.h index fc92e33..d7cda87 100644 --- a/third_party/concurrence/sync/mutex.h +++ b/third_party/concurrence/sync/mutex.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,12 @@ #ifndef VIRTUAL_MUTEX_H_ #define VIRTUAL_MUTEX_H_ +#include + #include "absl/container/flat_hash_set.h" +#include "third_party/concurrence/scheduler/scheduler.h" #include "sync.h" - -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" class SyncTracker; @@ -28,13 +30,17 @@ class VirtualMutex : public Sync { public: explicit VirtualMutex(SyncTracker *sync_tracker); ~VirtualMutex() override; + VirtualMutex(const VirtualMutex &) = delete; + VirtualMutex(VirtualMutex &&) = delete; + VirtualMutex &operator=(const VirtualMutex &) = delete; + VirtualMutex &operator=(VirtualMutex &&) = delete; void Lock(); bool TryLock(); void Unlock(); - bool Held(); - void AssertHeld(); + bool Held() const; + void AssertHeld() const; bool IsOwned() override; bool IsOwnedBy(ThreadHandle handle) override; diff --git a/third_party/concurrence/sync/mutex_test.cc b/third_party/concurrence/sync/mutex_test.cc index ed7a5d5..8a15845 100644 --- a/third_party/concurrence/sync/mutex_test.cc +++ b/third_party/concurrence/sync/mutex_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,16 +14,19 @@ #include "mutex.h" +#include #include -#include "scheduler/mock_scheduler.h" -#include "sync/tracker.h" +#include "third_party/concurrence/scheduler/mock_scheduler.h" +#include "third_party/concurrence/sync/tracker.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using ::testing::ElementsAre; using ::testing::IsEmpty; +bool is_verbose = false; + TEST(MutexTest, LockUnlock) { MockScheduler mock_scheduler; SyncTracker tracker(&mock_scheduler); diff --git a/third_party/concurrence/sync/rwlock.cc b/third_party/concurrence/sync/rwlock.cc index afeb8a9..148c573 100644 --- a/third_party/concurrence/sync/rwlock.cc +++ b/third_party/concurrence/sync/rwlock.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,16 +15,17 @@ #include "rwlock.h" #include +#include +#include #include #include "absl/container/flat_hash_map.h" #include "absl/hash/hash.h" -#include "absl/log/log.h" - -#include "scheduler/scheduler.h" +#include "third_party/concurrence/scheduler/scheduler.h" class SyncTracker; +extern bool is_verbose; void GetBacktrace(std::vector &stack); VirtualRwLock::VirtualRwLock(SyncTracker *sync_tracker) @@ -59,6 +60,7 @@ void VirtualRwLock::LockExclusive() { AddWaiter(); scheduler()->Block(); } + RemoveWaiter(); // mutex_.Lock can block and we might lose the race after someone else // took it added a reader @@ -68,9 +70,13 @@ void VirtualRwLock::LockExclusive() { } mutex_.Unlock(); } + // printf("VirtualRwLock::LockExclusive: readers %zu held %d\n", + // owners().size(), mutex_.Held()); } void VirtualRwLock::UnlockExclusive() { + // printf("VirtualRwLock::UnlockExclusive: readers %zu, held %d\n", + // owners().size(), mutex_.Held()); if (!mutex_.Held()) { abort(); } @@ -83,16 +89,24 @@ void VirtualRwLock::UnlockExclusive() { void VirtualRwLock::LockShared() { mutex_.Lock(); AddOwner(); + // printf("VirtualRwLock::LockShared: readers incremented to %zu\n", + // owners().size()); mutex_.Unlock(); } void VirtualRwLock::UnlockShared() { + // printf("VirtualRwLock::UnlockShared: before lock readers %zu\n", + // owners().size()); mutex_.Lock(); + // printf("VirtualRwLock::UnlockShared: after lock readers %zu\n", + // owners().size()); RemoveOwner(); mutex_.Unlock(); } void VirtualRwLock::ExclusiveToShared() { + // printf("VirtualRwLock::ExclusiveToShared(%p), readers %zu mutex held %d\n", + // this, owners().size(), mutex_.Held()); mutex_.AssertHeld(); if (!owners_.empty()) { abort(); @@ -100,9 +114,13 @@ void VirtualRwLock::ExclusiveToShared() { AddOwner(); mutex_.Unlock(); + // printf("VirtualRwLock::ExclusiveToShared(%p), readers %zu mutex held %d\n", + // this, owners().size(), mutex_.Held()); } void VirtualRwLock::Unlock() { + // printf("VirtualRwLock::Unlock(%p), readers %zu mutex held %d\n", this, + // owners().size(), mutex_.Held()); if (!IsShared()) { UnlockExclusive(); } else { @@ -122,12 +140,12 @@ bool VirtualRwLock::SharedToExclusive() { void VirtualRwLock::AddOwner() { if (IsOwnedBy(g_current_thread)) { - LOG(ERROR) << "VirtualRwLock is already owned by current thread!\n"; + std::cout << "VirtualRwLock is already owned by current thread!\n"; abort(); } owners_.insert(g_current_thread); - if (enable_debug_checks_) { - GetBacktrace(owner_backtraces()[g_current_thread]); + if (is_verbose) { + owner_backtraces()[g_current_thread] = std::make_unique(); } } @@ -136,11 +154,11 @@ void VirtualRwLock::RemoveOwner() { if (!erased) { abort(); } - if (enable_debug_checks_) { + if (is_verbose) { owner_backtraces().erase(g_current_thread); } if (owners_.empty()) { - ClearAndUnblockWaiters(); + WakeupWaiters(); } } diff --git a/third_party/concurrence/sync/rwlock.h b/third_party/concurrence/sync/rwlock.h index a2ce272..42d719c 100644 --- a/third_party/concurrence/sync/rwlock.h +++ b/third_party/concurrence/sync/rwlock.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,15 @@ #ifndef VIRTUAL_RWLOCK_H_ #define VIRTUAL_RWLOCK_H_ -#include "absl/container/flat_hash_set.h" +#include +#include +#include -#include "executor/executor.h" -#include "sync/mutex.h" -#include "sync/sync.h" +#include "third_party/concurrence/scheduler/scheduler.h" +#include "absl/container/flat_hash_set.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/sync/mutex.h" +#include "third_party/concurrence/sync/sync.h" class SyncTracker; @@ -29,6 +33,10 @@ class VirtualRwLock : public Sync { public: explicit VirtualRwLock(SyncTracker *sync_tracker); ~VirtualRwLock() override; + VirtualRwLock(const VirtualRwLock &) = delete; + VirtualRwLock(VirtualRwLock &&) = delete; + VirtualRwLock &operator=(const VirtualRwLock &) = delete; + VirtualRwLock &operator=(VirtualRwLock &&) = delete; bool TryLockExclusive(); void LockExclusive(); diff --git a/third_party/concurrence/sync/rwlock_test.cc b/third_party/concurrence/sync/rwlock_test.cc index 1d2b504..a82d438 100644 --- a/third_party/concurrence/sync/rwlock_test.cc +++ b/third_party/concurrence/sync/rwlock_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sync/rwlock.h" +#include "third_party/concurrence/sync/rwlock.h" +#include #include #include -#include "scheduler/mock_scheduler.h" -#include "sync/tracker.h" +#include "third_party/concurrence/scheduler/mock_scheduler.h" +#include "third_party/concurrence/sync/tracker.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/third_party/concurrence/sync/sync.cc b/third_party/concurrence/sync/sync.cc index 62edac0..40e9057 100644 --- a/third_party/concurrence/sync/sync.cc +++ b/third_party/concurrence/sync/sync.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,21 +14,25 @@ #include "sync.h" -#include #include +#include +#include +#include #include "absl/hash/hash.h" -#include "absl/log/log.h" +#include "absl/log/check.h" #include "absl/meta/type_traits.h" #include "absl/strings/str_format.h" -#include "backtrace/backtrace.h" -#include "scheduler/scheduler.h" -#include "sync/tracker.h" +#include "third_party/concurrence/backtrace/backtrace.h" +#include "third_party/concurrence/scheduler/scheduler.h" +#include "tracker.h" + +extern bool is_verbose; Sync::Sync(SyncTracker *sync_tracker) : sync_tracker_(sync_tracker), scheduler_(sync_tracker->scheduler()) { - if (enable_debug_checks_) { - GetBacktrace(construction_backtrace_); + if (is_verbose) { + construction_stacktrace_ = std::make_unique(); } } @@ -38,15 +42,26 @@ void Sync::AddWaiter() { if (IsOwnedBy(g_current_thread)) { HandleNestedWait(); } - waiters_.insert(g_current_thread); - if (enable_debug_checks_) { + // Remove our existing wait if we already are waiting to avoid duplication + // TODO(nedwill): add a unit test for this case + RemoveWaiter(); + waiters_.push_back(g_current_thread); + if (is_verbose) { DetectDeadlock(); } } -void Sync::ClearAndUnblockWaiters() { +void Sync::RemoveWaiter() { + auto it = std::find(waiters_.begin(), waiters_.end(), g_current_thread); + if (it == waiters_.end()) { + return; + } + waiters_.erase(it); +} + +void Sync::WakeupWaiters() { scheduler_->MakeAllRunnable(waiters_); - waiters_.clear(); + // waiters_.clear(); } // The current thread is now a waiter for this sync object. Check for deadlocks. @@ -66,43 +81,40 @@ void Sync::DetectDeadlock() { } void Sync::HandleNestedWait() { - if (enable_debug_checks_) { - std::vector backtrace; - GetBacktrace(backtrace); + if (is_verbose) { + auto stacktrace = std::make_unique(); - std::vector original_backtrace = - owner_backtraces_[g_current_thread]; + const auto& original_stacktrace = owner_backtraces_[g_current_thread]; - LOG(INFO) << absl::StrFormat("Nested wait for sync primitive %p\n", this); - LOG(INFO) << "\nOriginal ownership acquired:\n"; - PrintBacktraceFromStack(original_backtrace); - LOG(INFO) << "\nTried to wait again on the same object here:\n"; - PrintBacktraceFromStack(backtrace); + std::cout << absl::StrFormat("Nested wait for sync primitive %p\n", this); + std::cout << "\nOriginal ownership acquired:\n"; + original_stacktrace->Print(); + std::cout << "\nTried to wait again on the same object here:\n"; + stacktrace->Print(); } abort(); } void Sync::HandleBlockedMainThread() { - LOG(INFO) << absl::StrFormat( + std::cout << absl::StrFormat( "\nMain thread %s blocked\n", scheduler_->DescribeThreadHandle(g_current_thread)); - std::vector backtrace; - GetBacktrace(backtrace); - PrintBacktraceFromStack(backtrace); + auto stacktrace = std::make_unique(); + stacktrace->Print(); Sync *current = this; while (current && !current->owners().empty()) { // Only consider first owner since we only need one example dependency path ThreadHandle owner = *current->owners().begin(); Sync *blocking_object = sync_tracker_->GetObjectWithWaiter(owner); - LOG(INFO) << absl::StrFormat( + std::cout << absl::StrFormat( "\nBlocked by thread %s (IsRunnable %d) on object %p\n", scheduler_->DescribeThreadHandle(owner), scheduler_->IsRunnable(owner), blocking_object); scheduler_->PrintBacktrace(owner); - LOG(INFO) << "\nBacktrace where ownership was acquired\n"; + std::cout << "\nBacktrace where ownership was acquired\n"; auto it = owner_backtraces_.find(owner); if (it != owner_backtraces_.end()) { - PrintBacktraceFromStack(it->second); + it->second->Print(); } current = blocking_object; } diff --git a/third_party/concurrence/sync/sync.h b/third_party/concurrence/sync/sync.h index 9342f89..de3f950 100644 --- a/third_party/concurrence/sync/sync.h +++ b/third_party/concurrence/sync/sync.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,28 +17,35 @@ #ifndef CONCURRENCE_SYNC_H_ #define CONCURRENCE_SYNC_H_ +#include +#include #include #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/scheduler.h" -#include "executor/executor.h" - -class Scheduler; class SyncTracker; +class Scheduler; class Sync { public: explicit Sync(SyncTracker *sync_tracker); virtual ~Sync(); + Sync(const Sync &) = delete; + Sync(Sync &&) = delete; + Sync &operator=(const Sync &) = delete; + Sync &operator=(Sync &&) = delete; void AddWaiter(); - void ClearAndUnblockWaiters(); + void RemoveWaiter(); + void WakeupWaiters(); Scheduler *scheduler() { return scheduler_; } - const std::vector &construction_backtrace() { - return construction_backtrace_; + const std::unique_ptr &construction_stacktrace() { + return construction_stacktrace_; } virtual bool IsOwned() = 0; @@ -46,19 +53,15 @@ class Sync { // Some sync primitives have multiple owners virtual const absl::flat_hash_set &owners() = 0; - const absl::flat_hash_set &waiters() { return waiters_; } + const std::vector &waiters() { return waiters_; } - absl::flat_hash_map> &owner_backtraces() { + absl::flat_hash_map> &owner_backtraces() { return owner_backtraces_; } void HandleNestedWait(); void HandleBlockedMainThread(); - protected: - // Logs stacktraces and checks for deadlocks and nested locking - bool enable_debug_checks_; - private: void DetectDeadlock(); @@ -67,9 +70,9 @@ class Sync { // reference for a slight performance boost. Scheduler *scheduler_; - absl::flat_hash_set waiters_; - std::vector construction_backtrace_; - absl::flat_hash_map> owner_backtraces_; + std::vector waiters_; + std::unique_ptr construction_stacktrace_; + absl::flat_hash_map> owner_backtraces_; }; #endif diff --git a/third_party/concurrence/sync/tracker.cc b/third_party/concurrence/sync/tracker.cc index 13efdbb..632e5c8 100644 --- a/third_party/concurrence/sync/tracker.cc +++ b/third_party/concurrence/sync/tracker.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,17 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sync/tracker.h" +#include "third_party/concurrence/sync/tracker.h" +#include #include #include -#include "sync/mutex.h" -#include "sync/rwlock.h" -#include "sync/sync.h" +#include "third_party/concurrence/backtrace/backtrace.h" +#include "third_party/concurrence/sync/mutex.h" +#include "third_party/concurrence/sync/rwlock.h" +#include "third_party/concurrence/sync/sync.h" class Scheduler; +extern bool is_verbose; + SyncTracker::SyncTracker(Scheduler *scheduler) : scheduler_(scheduler) {} SyncTracker::~SyncTracker() = default; @@ -30,7 +34,7 @@ VirtualMutex *SyncTracker::AllocateMutex() { auto mutex = std::make_unique(this); VirtualMutex *ptr = mutex.get(); all_primitives_[ptr] = std::move(mutex); -#ifdef NDEBUG +#ifndef NDEBUG session_tracked_primitives_.insert(ptr); #endif return ptr; @@ -38,7 +42,7 @@ VirtualMutex *SyncTracker::AllocateMutex() { void SyncTracker::FreeMutex(VirtualMutex *mutex) { all_primitives_.erase(mutex); -#ifdef NDEBUG +#ifndef NDEBUG session_tracked_primitives_.erase(mutex); #endif } @@ -47,7 +51,7 @@ VirtualRwLock *SyncTracker::AllocateRwLock() { auto rwlock = std::make_unique(this); VirtualRwLock *ptr = rwlock.get(); all_primitives_[ptr] = std::move(rwlock); -#ifdef NDEBUG +#ifndef NDEBUG session_tracked_primitives_.insert(ptr); #endif return ptr; @@ -55,7 +59,7 @@ VirtualRwLock *SyncTracker::AllocateRwLock() { void SyncTracker::FreeRwLock(VirtualRwLock *rwlock) { all_primitives_.erase(rwlock); -#ifdef NDEBUG +#ifndef NDEBUG session_tracked_primitives_.erase(rwlock); #endif } @@ -75,7 +79,7 @@ absl::flat_hash_set SyncTracker::Owned(ThreadHandle handle) { Sync *SyncTracker::GetObjectWithWaiter(ThreadHandle handle) { for (const auto &it : all_primitives_) { Sync *sync = it.first; - if (sync->waiters().count(handle)) { + if (std::find(sync->waiters().begin(), sync->waiters().end(), handle) != sync->waiters().end()) { if (sync->IsOwnedBy(handle)) { abort(); } @@ -113,16 +117,18 @@ bool SyncTracker::FindDependencyPath(ThreadHandle source, } void SyncTracker::BeginSession() { -#ifdef NDEBUG +#ifndef NDEBUG session_tracked_primitives_.clear(); #endif } void SyncTracker::ReportSession() { -#ifdef NDEBUG - if (!enable_debug_checks_ || session_tracked_primitives_.empty()) { +#ifndef NDEBUG + if (!is_verbose || session_tracked_primitives_.empty()) { return; } + // TODO(nedwill): investigate if it makes sense to keep some allocated + return; printf( "SyncTracker Report: there are %lu sync primitives that were never " "freed.\n", @@ -130,13 +136,13 @@ void SyncTracker::ReportSession() { for (auto *sync : session_tracked_primitives_) { printf("Sync primitive was never freed. Created here:\n"); - PrintBacktraceFromStack(sync->construction_backtrace()); + sync->construction_stacktrace()->Print(); } #endif } void SyncTracker::FreeSession() { -#ifdef NDEBUG +#ifndef NDEBUG for (auto *sync : session_tracked_primitives_) { all_primitives_.erase(sync); } diff --git a/third_party/concurrence/sync/tracker.h b/third_party/concurrence/sync/tracker.h index 0b610a9..2be19f7 100644 --- a/third_party/concurrence/sync/tracker.h +++ b/third_party/concurrence/sync/tracker.h @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,15 @@ #include #include +#include +#include #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" - -#include "executor/executor.h" +#include "third_party/concurrence/executor/executor.h" +#include "third_party/concurrence/scheduler/scheduler.h" +#include "third_party/concurrence/sync/mutex.h" +#include "third_party/concurrence/sync/rwlock.h" class Scheduler; class Sync; @@ -57,7 +61,7 @@ class SyncTracker { private: Scheduler *scheduler_; absl::flat_hash_map> all_primitives_; -#ifdef NDEBUG +#ifndef NDEBUG absl::flat_hash_set session_tracked_primitives_; #endif }; diff --git a/tools/generate_syscall/BUILD.bazel b/tools/generate_syscall/BUILD.bazel new file mode 100644 index 0000000..2b210a2 --- /dev/null +++ b/tools/generate_syscall/BUILD.bazel @@ -0,0 +1,175 @@ +load("//third_party/xnu:options.bzl", "LIBKERN_DEFINES") +load("@rules_python//python:defs.bzl", "py_library", "py_binary", "py_test") + +py_library( + name = "upstream_syscall", + srcs = [ + "upstream_syscall.py", + ], +) + +py_binary( + name = "upstream_syscall_manager", + srcs = [ + "upstream_syscall_manager.py", + ], + deps = [ + ":upstream_syscall", + ], +) + +py_test( + name = "upstream_syscall_manager_test", + size = "small", + srcs = [ + "upstream_syscall_manager_test.py", + ], + deps = [ + "upstream_syscall_manager", + ], +) + +genrule( + name = "bsd_syscall_wrappers", + srcs = [ + "//third_party/xnu:bsd/kern/syscalls.master", + "templates/wrappers_header_preamble.h", + ], + outs = [ + "syscall_wrappers_generated.c", + "syscall_wrappers_generated.h", + ], + cmd = " ".join([ + "$(location upstream_syscall_manager)", + "-i $(location //third_party/xnu:bsd/kern/syscalls.master)", + "--wrappers $(location syscall_wrappers_generated.c)", + "--wrappers_header $(location syscall_wrappers_generated.h)", + "--wrappers_header_preamble $(location templates/wrappers_header_preamble.h)", + "-x nosys", + "-x enosys", + ] + LIBKERN_DEFINES), + tools = [":upstream_syscall_manager"], + visibility = [ + "//fuzz/target:__pkg__", + "//fuzz/xnu/bsd:__pkg__", + ], +) + +py_library( + name = "downstream_field", + srcs = [ + "downstream_field.py", + ], +) + +py_library( + name = "downstream_syscall_parser", + srcs = [ + "downstream_syscall_parser.py", + ], + deps = [ + ":downstream_field", + ], +) + +py_test( + name = "downstream_syscall_parser_test", + size = "small", + srcs = [ + "downstream_syscall_parser_test.py", + ], + deps = [ + ":downstream_syscall_parser", + ], +) + +py_library( + name = "downstream_syscall", + srcs = [ + "downstream_syscall.py", + ], + deps = [ + ":downstream_field", + ":downstream_syscall_parser", + ], +) + +py_test( + name = "downstream_syscall_test", + size = "small", + srcs = [ + "downstream_syscall_test.py", + ], + deps = [ + ":downstream_syscall", + ], +) + +py_binary( + name = "downstream_syscall_manager", + srcs = [ + "downstream_syscall_manager.py", + ], + deps = [ + ":downstream_syscall", + ":upstream_syscall_manager", + ], +) + +py_test( + name = "downstream_syscall_manager_test", + size = "small", + srcs = [ + "downstream_syscall_manager_test.py", + ], + deps = [ + ":downstream_syscall_manager", + ], +) + +genrule( + name = "bsd_syscall_callers", + srcs = [ + "//third_party/xnu:bsd/kern/syscalls.master", + "data/syscalls.defs", + ], + outs = [ + "call_bsd_syscalls_generated.cc", + "bsd_syscalls_generated.proto", + ], + cmd = " ".join([ + "$(location downstream_syscall_manager)", + "-i $(location data/syscalls.defs)", + "-u $(location //third_party/xnu:bsd/kern/syscalls.master)", + "--caller $(location call_bsd_syscalls_generated.cc)", + "--protos $(location bsd_syscalls_generated.proto)", + ] + LIBKERN_DEFINES), + tools = [":downstream_syscall_manager"], +) + +cc_library( + name = "call_bsd_syscalls_generated", + srcs = [ + ":call_bsd_syscalls_generated.cc", + ], + hdrs = [ + "call_bsd_syscall.h", + ], + visibility = ["//fuzz/handlers:__pkg__"], + deps = [ + "//fuzz/handlers:bsd", + ], +) + +proto_library( + name = "bsd_syscalls_proto", + srcs = ["bsd_syscalls_generated.proto"], + visibility = [ + "//fuzz/proto:__pkg__", + "//fuzz/target:__pkg__", + ], + deps = [ + "//fuzz/proto:bsd_enums_proto", + "//fuzz/proto:bsd_types_proto", + ], +) diff --git a/tools/generate_syscall/call_bsd_syscall.h b/tools/generate_syscall/call_bsd_syscall.h new file mode 100644 index 0000000..f87cc87 --- /dev/null +++ b/tools/generate_syscall/call_bsd_syscall.h @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef CALL_COMMAND_H_ +#define CALL_COMMAND_H_ + +#include +#include + +#include "tools/generate_syscall/bsd_syscalls_generated.pb.h" + +extern "C" { +void CallBsdSyscall(const BsdSyscall &syscall); +} + +#endif diff --git a/tools/generate_syscall/data/syscalls.defs b/tools/generate_syscall/data/syscalls.defs new file mode 100644 index 0000000..f879312 --- /dev/null +++ b/tools/generate_syscall/data/syscalls.defs @@ -0,0 +1,440 @@ +# Manually override types to work with codegen.sh + +# TODO(nedwill): add support for Mach syscalls + +# exit causes crashes +# exit(Int32 rval, Retval retval) + +fork(Retval retval) +read(FileDescriptor fd, EmptyBuffer[cast="user_addr_t", size_given_by=nbyte] cbuf, CappedSize nbyte, Ptr[field=Declared[c_type=long]] retval) +write(FileDescriptor fd, Buffer cbuf, SizeOf[container=cbuf] nbyte, Ptr[field=Declared[c_type=long]] retval) +open(Buffer path, Flags[proto_type=OpenFlags] flags, Flags[proto_type=PermissionMode] mode, Retval retval) +sys_close(FileDescriptor fd, Retval retval) +wait4(Int32 pid, Ptr[cast="user_addr_t", field=Declared[c_type=int]] status, Int32 options, Nullptr rusage, Retval retval) +link(Buffer path, Buffer link_arg, Retval retval) +unlink(Buffer path, Retval retval) +sys_chdir(Buffer path, Retval retval) +sys_fchdir(FileDescriptor fd, Retval retval) +mknod(Buffer path, Flags[proto_type=PermissionMode] mode, Int32 dev, Retval retval) +chmod(Buffer path, Flags[proto_type=PermissionMode] mode, Retval retval) +chown(Buffer path, Int32 uid, Int32 gid, Retval retval) +getfsstat(Nullptr buf, Int32 bufsize, Int32 flags, Retval retval) +getpid(Retval retval) +setuid(UInt32 uid, Retval retval) +getuid(Retval retval) +geteuid(Retval retval) +# ptrace(Int32 req, Int32 pid, (char *) UInt64 addr, Int32 data, Retval retval) +recvmsg(FileDescriptor s, Ptr[field=Declared[c_type="struct msghdr"]] msg, Flags[proto_type=MsgFlags] flags, Retval retval) +# sendmsg(FileDescriptor s, (caddr_t) RandomStruct[struct=msghdr] msg, Flags[proto_type=MsgFlags] flags, Retval retval) +recvfrom(FileDescriptor s, EmptyBuffer[cast="void *", size_given_by=len] buf, CappedSize len, Flags[proto_type=MsgFlags] flags, EmptyArray[c_type="struct sockaddr",size_given_by=fromlenaddr] from, Ptr[cast="int *", field=CappedSize] fromlenaddr, Retval retval) +accept(FileDescriptor s, EmptyBuffer[cast=caddr_t, size_given_by=anamelen] name, Ptr[field=Socklen] anamelen, Retval retval) +getpeername(FileDescriptor fdes, EmptyBuffer[size_given_by=alen] asa, Ptr[field=Socklen] alen, Retval retval) +getsockname(FileDescriptor fdes, EmptyBuffer[size_given_by=alen] asa, Ptr[field=Socklen] alen, Retval retval) +access(Buffer path, Flags[proto_type=PermissionMode] flags, Retval retval) +chflags(Buffer[cast="char *"] path, Int32 flags, Retval retval) +fchflags(FileDescriptor fd, Int32 flags, Retval retval) +sync(Retval retval) +kill(Int32 pid, Int32 signum, Bool posix, Retval retval) +getppid(Retval retval) +sys_dup(FileDescriptor fd, Retval retval) +pipe(EmptyPipeFds retval) +getegid(Retval retval) +# sigaction(Int32 signum, RandomStruct[struct=__sigaction] nsa, Ptr[field=Declared[c_type="struct __sigaction"]] osa, Retval retval) +getgid(Retval retval) +# sigprocmask(Int32 how, Nullptr mask, Nullptr omask, Retval retval) +getlogin(EmptyBuffer[size_given_by=namelen] namebuf, CappedSize namelen, Retval retval) +setlogin(Buffer[cast="char *"] namebuf, Retval retval) +acct(Buffer[cast="char *"] path, Retval retval) +# sigpending(Nullptr osv, Retval retval) +# sigaltstack(RandomStruct[struct=sigaltstack] nss, Ptr[field=Declared[c_type="struct sigaltstack"]] oss, Retval retval) +# ioctl(Int32 fd, Literal[value=0] com, Nullptr data, Retval retval) +# reboot(Int32 opt, Buffer msg, Retval retval) +revoke(Buffer[cast="char *"] path, Retval retval) +symlink(Buffer[cast="char *"] path, Buffer[cast="char *"] link, Retval retval) +readlink(Buffer[cast="char *"] path, EmptyBuffer[size_given_by=count] buf, CappedSize count, Retval retval) +# execve(Buffer fname, Nullptr argp, Nullptr envp, Retval retval) +umask(Flags[proto_type=PermissionMode] newmask, Retval retval) +chroot(Buffer path, Retval retval) +msync(UInt64[cast=caddr_t] addr, UInt64 len, Flags[proto_type=MsyncFlags] flags, Retval retval) +munmap(UInt64[cast=caddr_t] addr, UInt64 len, Retval retval) +mprotect(UInt64[cast=caddr_t] addr, UInt64 len, Flags[proto_type=MmapProt] prot, Retval retval) +madvise(UInt64[cast=caddr_t] addr, UInt64 len, Enum[proto_type=MadviseFlags] behav, Retval retval) +mincore(UInt64[cast=user_addr_t] addr, CappedSize len, EmptyBuffer[size_given_by=len, cast="user_addr_t"] vec, Retval retval) +# getgroups(Literal[value=0] gidsetsize, Nullptr gidset, Retval retval) +# setgroups(Literal[value=0] gidsetsize, Nullptr gidset, Retval retval) +getpgrp(Retval retval) +setpgid(Int32 pid, Int32 pgid, Retval retval) +# setitimer(UInt32 which, RandomStruct[struct=itimerval] itv, Ptr[field=Declared[c_type="struct itimerval"]] oitv, Retval retval) +swapon(Retval retval) +# getitimer(UInt32 which, Ptr[field=Declared[c_type="struct itimerval"]] itv, Retval retval) +sys_getdtablesize(Retval retval) +sys_dup2(FileDescriptor from, FileDescriptor to, Retval retval) +# sys_fcntl(FileDescriptor fd, Int32 cmd, Int64 arg, Retval retval) +select(FileDescriptor nd, Ptr[field=Declared[c_type="unsigned int"]] in, Ptr[field=Declared[c_type="unsigned int"]] ou, Ptr[field=Declared[c_type="unsigned int"]] ex, RandomStruct[struct=timeval] tv, Retval retval) +fsync(FileDescriptor fd, Retval retval) +# setpriority(Int32 which, UInt32 who, Int32 prio, Retval retval) +socket(Enum[proto_type=Domain] domain, Enum[proto_type=SocketType] type, Enum[proto_type=Protocol] protocol, Retval retval) +connect(FileDescriptor s, SockAddr name, SizeOf[container=name] namelen, Retval retval) +# getpriority(Int32 which, UInt32 who, Retval retval) +bind(FileDescriptor s, SockAddr name, SizeOf[container=name] namelen, Retval retval) +setsockopt(FileDescriptor s, Enum[proto_type=Protocol] level, Enum[proto_type=SocketOptName] name, Buffer[cast=caddr_t] val, SizeOf[container=val] valsize, Retval retval) +listen(FileDescriptor s, Int32 backlog, Retval retval) +# sigsuspend(UInt32 mask, Retval retval) +gettimeofday(Ptr[field=Declared[c_type="struct timeval"]] tp, Ptr[field=Declared[c_type="struct fuzz_timezone"]] tzp, Ptr[field=Declared[c_type="unsigned long"]] mach_absolute_time, Retval retval) +getrusage(Enum[proto_type=RUsageWho] who, Ptr[field=Declared[c_type="struct rusage"]] rusage, Retval retval) +getsockopt(FileDescriptor s, Enum[proto_type=Protocol] level, Enum[proto_type=SocketOptName] name, EmptyBuffer[cast=caddr_t, size_given_by=avalsize] val, Ptr[field=Socklen] avalsize, Retval retval) +# readv(Int32 fd, Nullptr iovp, Literal[value=0] iovcnt, Ptr[field=Declared[c_type="unsigned long"]] retval) +# writev(Int32 fd, Nullptr iovp, Literal[value=0] iovcnt, Ptr[field=Declared[c_type="unsigned long"]] retval) +settimeofday(RandomStruct[struct=timeval] tv, RandomStruct[struct=fuzz_timezone] tzp, Retval retval) +fchown(FileDescriptor fd, Int32 uid, Int32 gid, Retval retval) +fchmod(FileDescriptor fd, Flags[proto_type=PermissionMode] mode, Retval retval) +setreuid(UInt32 ruid, UInt32 euid, Retval retval) +setregid(UInt32 rgid, UInt32 egid, Retval retval) +rename(Buffer[cast="char *"] from, Buffer[cast="char *"] to, Retval retval) +sys_flock(FileDescriptor fd, Flags[proto_type=FlockOp] how, Retval retval) +mkfifo(Buffer path, Flags[proto_type=PermissionMode] mode, Retval retval) +sendto(FileDescriptor s, Buffer[cast=caddr_t] buf, SizeOf[container=buf] len, Flags[proto_type=MsgFlags] flags, SockAddr to, SizeOf[container=to] tolen, Retval retval) +shutdown(FileDescriptor s, Enum[proto_type=ShutdownHow] how, Retval retval) +socketpair(Enum[proto_type=Domain] domain, Enum[proto_type=SocketType] type, Enum[proto_type=Protocol] protocol, EmptySocketPair rsv, Retval retval) +mkdir(Buffer path, Flags[proto_type=PermissionMode] mode, Retval retval) +rmdir(Buffer[cast="char *"] path, Retval retval) +# utimes(Buffer path, Nullptr tptr, Retval retval) +# futimes(Int32 fd, Nullptr tptr, Retval retval) +adjtime(RandomStruct[struct=timeval] delta, Ptr[field=Declared[c_type="struct timeval"]] olddelta, Retval retval) +gethostuuid(UUID uuid_buf, RandomStruct[struct=timespec] timeoutp, Retval retval) +setsid(Retval retval) +getpgid(Int32 pid, Retval retval) +setprivexec(Int32 flag, Retval retval) +pread(FileDescriptor fd, EmptyBuffer[cast="user_addr_t", size_given_by=nbyte] buf, CappedSize nbyte, UInt64 offset, Ptr[field=Declared[c_type=long]] retval) +pwrite(FileDescriptor fd, Buffer buf, SizeOf[container=buf] nbyte, UInt64 offset, Ptr[field=Declared[c_type=long]] retval) +# nfssvc(Int32 flag, Buffer argp, Retval retval) +# statfs(Buffer path, Ptr[field=Declared[c_type="struct statfs"]] buf, Retval retval) +# fstatfs(Int32 fd, Ptr[field=Declared[c_type="struct statfs"]] buf, Retval retval) +# unmount(Buffer path, Int32 flags, Retval retval) +# getfh(Buffer fname, RandomStruct[struct=fhandle] fhp, Retval retval) +# quotactl(Buffer path, Int32 cmd, Int32 uid, Buffer arg, Retval retval) +# mount(Buffer type, Buffer path, Int32 flags, Buffer data, Retval retval) +# csops(Int32 pid, UInt32 ops, EmptyBuffer[size_given_by=usersize] useraddr, UInt64 usersize, Retval retval) +# csops_audittoken(Int32 pid, UInt32 ops, EmptyBuffer[size_given_by=usersize] useraddr, UInt64 usersize, RandomStruct[struct=audit_token_t] uaudittoken, Retval retval) +# waitid(Enum[proto_type=IdType] idtype, UInt32 id, RandomStruct[struct=siginfo_t] infop, Int32 options, Retval retval) +# kdebug_typefilter(Nullptr addr, Nullptr size, Retval retval) +# kdebug_trace_string(UInt32 debugid, UInt64 str_id, Nullptr str, Ptr[field=Declared[c_type="unsigned long"]] retval) +# kdebug_trace64(UInt32 code, UInt64 arg1, UInt64 arg2, UInt64 arg3, UInt64 arg4, Retval retval) +# kdebug_trace(UInt32 code, UInt64 arg1, UInt64 arg2, UInt64 arg3, UInt64 arg4, Retval retval) +# setgid(UInt32 gid, Retval retval) +# setegid(UInt32 egid, Retval retval) +# seteuid(UInt32 euid, Retval retval) +# sigreturn(RandomStruct[struct=ucontext] uctx, Int32 infostyle, Nullptr token, Retval retval) +# thread_selfcounts(UInt32 kind, EmptyBuffer[size_given_by=nbytes] buf, UInt64 nbytes, Retval retval) +# fdatasync(Int32 fd, Retval retval) +# stat(Buffer path, Ptr[field=Declared[c_type="struct stat"]]] ub, Retval retval) +# sys_fstat(Int32 fd, Ptr[field=Declared[c_type="struct stat"]]] ub, Retval retval) +# lstat(Buffer path, Ptr[field=Declared[c_type="struct stat"]]] ub, Retval retval) +# pathconf(Buffer path, Int32 name, Retval retval) +# sys_fpathconf(Int32 fd, Int32 name, Retval retval) +# getrlimit(UInt32 which, Ptr[field=Declared[c_type="struct rlimit"]] rlp, Retval retval) +setrlimit(Enum[proto_type=RlimitWhich] which, RLimit rlp, Retval retval) +# getdirentries(Int32 fd, EmptyBuffer[size_given_by=count] buf, UInt32 count, Ptr[field=Declared[c_type=long]] basep, Retval retval) +# mmap(UInt64 addr, UInt64 len, Int32 prot, Int32 flags, Int32 fd, UInt64 pos, Ptr[field=Declared[c_type="unsigned long"]] retval) +# lseek(Int32 fd, UInt64 offset, Int32 whence, Ptr[field=Declared[c_type="unsigned long"]] retval) +# truncate(Buffer path, UInt64 length, Retval retval) +# ftruncate(Int32 fd, UInt64 length, Retval retval) +# sysctl(Nullptr name, Literal[value=0] namelen, Nullptr old, Nullptr oldlenp, Nullptr new_arg, Literal[value=0] newlen, Retval retval) +# mlock(UInt64 addr, UInt64 len, Retval retval) +# munlock(UInt64 addr, UInt64 len, Retval retval) +# undelete(Buffer path, Retval retval) +# open_dprotected_np(Buffer path, Int32 flags, Int32 class_arg, Int32 dpflags, Int32 mode, Retval retval) +# fsgetpath_ext(Buffer buf, SizeOf[container=buf] bufsize, Nullptr fsid, UInt64 objid, UInt32 options, Ptr[field=Declared[c_type="unsigned long"]] retval) +# getattrlist(Buffer path, Ptr[field=Declared[c_type="struct attrlist"]] alist, EmptyBuffer[size_given_by=CappedSize] attributeBuffer, UInt64 CappedSize, UInt64 options, Retval retval) +# setattrlist(Buffer path, RandomStruct[struct=attrlist] alist, Buffer attributeBuffer, SizeOf[container=attributeBuffer] CappedSize, UInt64 options, Retval retval) +# getdirentriesattr(Int32 fd, Ptr[field=Declared[c_type="struct attrlist"]] alist, EmptyBuffer[size_given_by=CappedSize] attributeBuffer, UInt64 CappedSize, Ptr[field=Declared[c_type="unsigned long"]] count, Ptr[field=Declared[c_type="unsigned long"]] basep, Ptr[field=Declared[c_type="unsigned long"]] newstate, UInt64 options, Retval retval) +# exchangedata(Buffer path1, Buffer path2, UInt64 options, Retval retval) +# searchfs(Buffer path, RandomStruct[struct=fssearchblock] searchblock, Ptr[field=Declared[c_type="unsigned int"]] nummatches, UInt32 scriptcode, UInt32 options, Ptr[field=Declared[c_type="struct searchstate"]] state, Retval retval) +# delete(Buffer path, Retval retval) +# copyfile(Buffer from, Buffer to, Int32 mode, Int32 flags, Retval retval) +# fgetattrlist(Int32 fd, Ptr[field=Declared[c_type="struct attrlist"]] alist, EmptyBuffer[size_given_by=CappedSize] attributeBuffer, UInt64 CappedSize, UInt64 options, Retval retval) +# fsetattrlist(Int32 fd, RandomStruct[struct=attrlist] alist, Buffer attributeBuffer, SizeOf[container=attributeBuffer] CappedSize, UInt64 options, Retval retval) +# poll(RandomStruct[struct=pollfd] fds, Literal[value=1] nfds, Int32 timeout, Retval retval) +# getxattr(Buffer path, Buffer attrname, EmptyBuffer[size_given_by=size] value, UInt64 size, UInt32 position, Int32 options, Ptr[field=Declared[c_type="unsigned long"]] retval) +# fgetxattr(Int32 fd, Buffer attrname, EmptyBuffer[size_given_by=size] value, UInt64 size, UInt32 position, Int32 options, Ptr[field=Declared[c_type="unsigned long"]] retval) +# setxattr(Buffer path, Buffer attrname, Buffer value, SizeOf[container=value] size, UInt32 position, Int32 options, Retval retval) +# fsetxattr(Int32 fd, Buffer attrname, Buffer value, SizeOf[container=value] size, UInt32 position, Int32 options, Retval retval) +# removexattr(Buffer path, Buffer attrname, Int32 options, Retval retval) +# fremovexattr(Int32 fd, Buffer attrname, Int32 options, Retval retval) +# listxattr(Buffer path, EmptyBuffer[size_given_by=bufsize] namebuf, UInt64 bufsize, Int32 options, Ptr[field=Declared[c_type="unsigned long"]] retval) +# flistxattr(Int32 fd, EmptyBuffer[size_given_by=bufsize] namebuf, UInt64 bufsize, Int32 options, Ptr[field=Declared[c_type="unsigned long"]] retval) +# fsctl(Buffer path, UInt64 cmd, Buffer data, UInt32 options, Retval retval) +# initgroups(UInt32 gidsetsize, Ptr[field=Declared[c_type=int]] gidset, Int32 gmuid, Retval retval) + +# Carelessly find-replacing the rest! + +# posix_spawn(Ptr[field=Declared[c_type=int]] pid, Buffer path, Nullptr adesc, Nullptr argv, Nullptr envp, Retval retval) +# ffsctl(Int32 fd, UInt64 cmd, Buffer data, UInt32 options, Retval retval) +# nfsclnt(Int32 flag, Nullptr argp, Retval retval) +# fhopen(RandomStruct[struct=fhandle] u_fhp, Int32 flags, Retval retval) +# minherit(UInt64 addr, UInt64 len, Int32 inherit, Retval retval) +# semsys(UInt32 which, Int32 a2, Int32 a3, Int32 a4, Int32 a5, Retval retval) +# msgsys(UInt32 which, Int32 a2, Int32 a3, Int32 a4, Int32 a5, Retval retval) +# shmsys(UInt32 which, Int32 a2, Int32 a3, Int32 a4, Retval retval) +# semctl(Int32 semid, Int32 semnum, Int32 cmd, user_addr_t arg, Retval retval) +# semget(key_t key, Int32 nsems, Int32 semflg, Retval retval) +# semop(Int32 semid, RandomStruct[struct=sembuf] sops, Int32 nsops, Retval retval) +# msgctl(Int32 msqid, Int32 cmd, RandomStruct[struct=msqid_ds] buf, Retval retval) +# msgget(key_t key, Int32 msgflg, Retval retval) +# msgsnd(Int32 msqid, void *msgp, size_t msgsz, Int32 msgflg, Retval retval) +# msgrcv(Int32 msqid, void *msgp, size_t msgsz, Int64 msgtyp, Int32 msgflg, Ptr[field=Declared[c_type="unsigned long"]] retval) +# shmat(Int32 shmid, void *shmaddr, Int32 shmflg, Ptr[field=Declared[c_type="unsigned long"]] retval) +# shmctl(Int32 shmid, Int32 cmd, RandomStruct[struct=shmid_ds] buf, Retval retval) +# shmdt(void *shmaddr, Retval retval) +# shmget(key_t key, size_t size, Int32 shmflg, Retval retval) +# shm_open(char *name, Int32 oflag, Int32 mode, Retval retval) +# shm_unlink(char *name, Retval retval) +# sem_open(char *name, Int32 oflag, Int32 mode, Int32 value, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sem_close(fuzz_sem_t *sem, Retval retval) +# sem_unlink(char *name, Retval retval) +# sem_wait(fuzz_sem_t *sem, Retval retval) +# sem_trywait(fuzz_sem_t *sem, Retval retval) +# sem_post(fuzz_sem_t *sem, Retval retval) +# sys_sysctlbyname(char *name, size_t namelen, void *old, size_t *oldlenp, void *new_arg, size_t newlen, Retval retval) +# open_extended(Buffer path, Int32 flags, UInt32 uid, UInt32 gid, Int32 mode, user_addr_t xsecurity, Retval retval) +# umask_extended(Int32 newmask, user_addr_t xsecurity, Retval retval) +# stat_extended(Buffer path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, Retval retval) +# lstat_extended(Buffer path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, Retval retval) +# sys_fstat_extended(Int32 fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, Retval retval) +# chmod_extended(Buffer path, UInt32 uid, UInt32 gid, Int32 mode, user_addr_t xsecurity, Retval retval) +# fchmod_extended(Int32 fd, UInt32 uid, UInt32 gid, Int32 mode, user_addr_t xsecurity, Retval retval) +# access_extended(user_addr_t entries, size_t size, user_addr_t results, UInt32 uid, Retval retval) +# settid(UInt32 uid, UInt32 gid, Retval retval) +# gettid(Ptr[field=Declared[c_type=int]] uidp, Ptr[field=Declared[c_type=int]] gidp, Retval retval) +# setsgroups(Int32 setlen, user_addr_t guidset, Retval retval) +# getsgroups(user_addr_t setlen, user_addr_t guidset, Retval retval) +# setwgroups(Int32 setlen, user_addr_t guidset, Retval retval) +# getwgroups(user_addr_t setlen, user_addr_t guidset, Retval retval) +# mkfifo_extended(Buffer path, UInt32 uid, UInt32 gid, Int32 mode, user_addr_t xsecurity, Retval retval) +# mkdir_extended(Buffer path, UInt32 uid, UInt32 gid, Int32 mode, user_addr_t xsecurity, Retval retval) +# identitysvc(Int32 opcode, user_addr_t message, Retval retval) +# shared_region_check_np(Ptr[field=Declared[c_type="unsigned long"]] start_address, Retval retval) +# vm_pressure_monitor(Int32 wait_for_pressure, Int32 nsecs_monitored, Ptr[field=Declared[c_type="unsigned int"]] pages_reclaimed, Retval retval) +# psynch_rw_longrdlock(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_rw_yieldwrlock(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_rw_downgrade(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Retval retval) +# psynch_rw_upgrade(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_mutexwait(user_addr_t mutex, UInt32 mgen, UInt32 ugen, UInt64 tid, UInt32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_mutexdrop(user_addr_t mutex, UInt32 mgen, UInt32 ugen, UInt64 tid, UInt32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_cvbroad(user_addr_t cv, UInt64 cvlsgen, UInt64 cvudgen, UInt32 flags, user_addr_t mutex, UInt64 mugen, UInt64 tid, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_cvsignal(user_addr_t cv, UInt64 cvlsgen, UInt32 cvugen, Int32 thread_port, user_addr_t mutex, UInt64 mugen, UInt64 tid, UInt32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_cvwait(user_addr_t cv, UInt64 cvlsgen, UInt32 cvugen, user_addr_t mutex, UInt64 mugen, UInt32 flags, int64_t sec, UInt32 nsec, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_rw_rdlock(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_rw_wrlock(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_rw_unlock(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# psynch_rw_unlock2(user_addr_t rwlock, UInt32 lgenval, UInt32 ugenval, UInt32 rw_wc, Int32 flags, Ptr[field=Declared[c_type="unsigned int"]] retval) +# getsid(Int32 pid, Retval retval) +# settid_with_pid(Int32 pid, Int32 assume, Retval retval) +# psynch_cvclrprepost(user_addr_t cv, UInt32 cvgen, UInt32 cvugen, UInt32 cvsgen, UInt32 prepocnt, UInt32 preposeq, UInt32 flags, Retval retval) +# aio_fsync(Int32 op, user_addr_t aiocbp, Retval retval) +# aio_return(user_addr_t aiocbp, Ptr[field=Declared[c_type="unsigned long"]] retval) +# aio_suspend(user_addr_t aiocblist, Int32 nent, user_addr_t timeoutp, Retval retval) +# aio_cancel(Int32 fd, user_addr_t aiocbp, Retval retval) +# aio_error(user_addr_t aiocbp, Retval retval) +# aio_read(user_addr_t aiocbp, Retval retval) +# aio_write(user_addr_t aiocbp, Retval retval) +# lio_listio(Int32 mode, user_addr_t aiocblist, Int32 nent, user_addr_t sigp, Retval retval) +# iopolicysys(Int32 cmd, void *arg, Retval retval) +# process_policy(Int32 scope, Int32 action, Int32 policy, Int32 policy_subtype, user_addr_t attrp, Int32 target_pid, UInt64 target_threadid, Retval retval) +# mlockall(Int32 how, Retval retval) +# munlockall(Int32 how, Retval retval) +# issetugid(Retval retval) +# __pthread_kill(Int32 thread_port, Int32 sig, Retval retval) +# __pthread_sigmask(Int32 how, user_addr_t set, user_addr_t oset, Retval retval) +# __sigwait(user_addr_t set, user_addr_t sig, Retval retval) +# __disable_threadsignal(Int32 value, Retval retval) +# __pthread_markcancel(Int32 thread_port, Retval retval) +# __pthread_canceled(Int32 action, Retval retval) +# __semwait_signal(Int32 cond_sem, Int32 mutex_sem, Int32 timeout, Int32 relative, int64_t tv_sec, int32_t tv_nsec, Retval retval) +# proc_info(int32_t callnum, int32_t pid, UInt32 flavor, UInt64 arg, user_addr_t buffer, int32_t CappedSize, Retval retval) +# sendfile(Int32 fd, Int32 s, UInt64 offset, Ptr[field=Declared[c_type="unsigned long"]] nbytes, RandomStruct[struct=sf_hdtr] hdtr, Int32 flags, Retval retval) +# stat64(Buffer path, Ptr[field=Declared[c_type="struct stat64"]] ub, Retval retval) +# sys_fstat64(Int32 fd, Ptr[field=Declared[c_type="struct stat64"]] ub, Retval retval) +# lstat64(Buffer path, Ptr[field=Declared[c_type="struct stat64"]] ub, Retval retval) +# stat64_extended(Buffer path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, Retval retval) +# lstat64_extended(Buffer path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, Retval retval) +# sys_fstat64_extended(Int32 fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, Retval retval) +# getdirentries64(Int32 fd, void *buf, user_size_t bufsize, Ptr[field=Declared[c_type="unsigned long"]] position, Ptr[field=Declared[c_type="unsigned long"]] retval) +# statfs64(Buffer path, RandomStruct[struct=statfs64] buf, Retval retval) +# fstatfs64(Int32 fd, RandomStruct[struct=statfs64] buf, Retval retval) +# getfsstat64(EmptyBuffer[size_given_by=bufsize] buf, Int32 bufsize, Int32 flags, Retval retval) +# __pthread_chdir(Buffer path, Retval retval) +# __pthread_fchdir(Int32 fd, Retval retval) +# audit(void *record, Int32 length, Retval retval) +# auditon(Int32 cmd, void *data, Int32 length, Retval retval) +# getauid(au_Ptr[field=Declared[c_type=int]] auid, Retval retval) +# setauid(au_Ptr[field=Declared[c_type=int]] auid, Retval retval) +# getaudit_addr(RandomStruct[struct=auditinfo_addr] auditinfo_addr, Int32 length, Retval retval) +# setaudit_addr(RandomStruct[struct=auditinfo_addr] auditinfo_addr, Int32 length, Retval retval) +# auditctl(Buffer path, Retval retval) +# bsdthread_create(user_addr_t func, user_addr_t func_arg, user_addr_t stack, user_addr_t pthread, UInt32 flags, Ptr[field=Declared[c_type="unsigned long"]] retval) +# bsdthread_terminate(user_addr_t stackaddr, size_t freesize, UInt32 port, user_addr_t sema_or_ulock, Retval retval) +kqueue(Retval retval) +# kevent(Int32 fd, RandomStruct[struct=kevent] changelist, Int32 nchanges, RandomStruct[struct=kevent] eventlist, Int32 nevents, RandomStruct[struct=timespec] timeout, Retval retval) +# lchown(Buffer path, UInt32 owner, UInt32 group, Retval retval) +# bsdthread_register(user_addr_t threadstart, user_addr_t wqthread, UInt32 flags, user_addr_t stack_addr_hint, user_addr_t targetconc_ptr, UInt32 dispatchqueue_offset, UInt32 tsd_offset, Retval retval) +# workq_open(Retval retval) +# workq_kernreturn(Int32 options, user_addr_t item, Int32 affinity, Int32 prio, Retval retval) +# kevent64(Int32 fd, RandomStruct[struct=kevent64_s] changelist, Int32 nchanges, RandomStruct[struct=kevent64_s] eventlist, Int32 nevents, Int32 flags, RandomStruct[struct=timespec] timeout, Retval retval) +# __old_semwait_signal(Int32 cond_sem, Int32 mutex_sem, Int32 timeout, Int32 relative, RandomStruct[struct=timespec] ts, Retval retval) +# __old_semwait_signal_nocancel(Int32 cond_sem, Int32 mutex_sem, Int32 timeout, Int32 relative, RandomStruct[struct=timespec] ts, Retval retval) +# thread_selfid(Ptr[field=Declared[c_type="unsigned long"]] retval) +# ledger(Int32 cmd, Nullptr arg1, Nullptr arg2, Nullptr arg3, Retval retval) +# kevent_qos(Int32 fd, RandomStruct[struct=kevent_qos_s] changelist, Int32 nchanges, RandomStruct[struct=kevent_qos_s] eventlist, Int32 nevents, void *data_out, size_t *data_available, Int32 flags, Retval retval) +# kevent_id(UInt64 id, RandomStruct[struct=kevent_qos_s] changelist, Int32 nchanges, RandomStruct[struct=kevent_qos_s] eventlist, Int32 nevents, void *data_out, size_t *data_available, Int32 flags, Retval retval) +# __mac_execve(char *fname, char **argp, char **envp, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_syscall(char *policy, Int32 call, user_addr_t arg, Retval retval) +# __mac_get_file(Buffer path_p, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_set_file(Buffer path_p, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_get_link(Buffer path_p, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_set_link(Buffer path_p, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_get_proc(RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_set_proc(RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_get_fd(Int32 fd, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_set_fd(Int32 fd, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_get_pid(Int32 pid, RandomStruct[struct=mac] mac_p, Retval retval) +# pselect(Int32 nd, Ptr[field=Declared[c_type="unsigned int"]] in, Ptr[field=Declared[c_type="unsigned int"]] ou, Ptr[field=Declared[c_type="unsigned int"]] ex, RandomStruct[struct=timespec] ts, Ptr[field=Declared[c_type="unsigned int"]] mask, Retval retval) +# pselect_nocancel(Int32 nd, Ptr[field=Declared[c_type="unsigned int"]] in, Ptr[field=Declared[c_type="unsigned int"]] ou, Ptr[field=Declared[c_type="unsigned int"]] ex, RandomStruct[struct=timespec] ts, Ptr[field=Declared[c_type="unsigned int"]] mask, Retval retval) +read_nocancel(FileDescriptor fd, EmptyBuffer[cast="user_addr_t", size_given_by=nbyte] cbuf, CappedSize nbyte, Ptr[field=Declared[c_type=long]] retval) +write_nocancel(FileDescriptor fd, Buffer cbuf, SizeOf[container=cbuf] nbyte, Ptr[field=Declared[c_type=long]] retval) +open_nocancel(Buffer path, Int32 flags, Int32 mode, Retval retval) +sys_close_nocancel(FileDescriptor fd, Retval retval) +wait4_nocancel(Int32 pid, Ptr[cast="user_addr_t", field=Declared[c_type=int]] status, Int32 options, Nullptr rusage, Retval retval) +recvmsg_nocancel(FileDescriptor s, Ptr[field=Declared[c_type="struct msghdr"]] msg, Flags[proto_type=MsgFlags] flags, Retval retval) + +# TODO(nedwill): model msghdr so we do not segfault when reading pointers from it +# sendmsg_nocancel(FileDescriptor s, (caddr_t) RandomStruct[struct=msghdr] msg, Flags[proto_type=MsgFlags] flags, Retval retval) + +recvfrom_nocancel(FileDescriptor s, EmptyBuffer[cast="void *", size_given_by=len] buf, CappedSize len, Flags[proto_type=MsgFlags] flags, Nullptr[cast="struct sockaddr *"] from, Nullptr[cast="int *"] fromlenaddr, Retval retval) +accept_nocancel(FileDescriptor s, EmptyBuffer[cast=caddr_t, size_given_by=anamelen] name, Ptr[field=Socklen] anamelen, Retval retval) +# msync_nocancel(Nullptr addr, size_t len, Int32 flags, Retval retval) +# sys_fcntl_nocancel(Int32 fd, Int32 cmd, Int64 arg, Retval retval) +# select_nocancel(Int32 nd, Ptr[field=Declared[c_type="unsigned int"]] in, Ptr[field=Declared[c_type="unsigned int"]] ou, Ptr[field=Declared[c_type="unsigned int"]] ex, RandomStruct[struct=timeval] tv, Retval retval) +# fsync_nocancel(Int32 fd, Retval retval) +connect_nocancel(FileDescriptor s, SockAddr name, SizeOf[container=name] namelen, Retval retval) +# sigsuspend_nocancel(UInt32 mask, Retval retval) +# readv_nocancel(Int32 fd, RandomStruct[struct=iovec] iovp, UInt32 iovcnt, Ptr[field=Declared[c_type="unsigned long"]] retval) +# writev_nocancel(Int32 fd, RandomStruct[struct=iovec] iovp, UInt32 iovcnt, Ptr[field=Declared[c_type="unsigned long"]] retval) +sendto_nocancel(FileDescriptor s, Buffer[cast=caddr_t] buf, SizeOf[container=buf] len, Flags[proto_type=MsgFlags] flags, SockAddr to, SizeOf[container=to] tolen, Retval retval) +pread_nocancel(FileDescriptor fd, EmptyBuffer[cast="user_addr_t", size_given_by=nbyte] buf, CappedSize nbyte, UInt64 offset, Ptr[field=Declared[c_type=long]] retval) +pwrite_nocancel(FileDescriptor fd, Buffer buf, SizeOf[container=buf] nbyte, UInt64 offset, Ptr[field=Declared[c_type=long]] retval) +# waitid_nocancel(Enum[proto_type=IdType] idtype, UInt32 id, siginfo_t *infop, Int32 options, Retval retval) +# poll_nocancel(RandomStruct[struct=pollfd] fds, UInt32 nfds, Int32 timeout, Retval retval) +# msgsnd_nocancel(Int32 msqid, void *msgp, size_t msgsz, Int32 msgflg, Retval retval) +# msgrcv_nocancel(Int32 msqid, void *msgp, size_t msgsz, Int64 msgtyp, Int32 msgflg, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sem_wait_nocancel(fuzz_sem_t *sem, Retval retval) +# aio_suspend_nocancel(user_addr_t aiocblist, Int32 nent, user_addr_t timeoutp, Retval retval) +# __sigwait_nocancel(user_addr_t set, user_addr_t sig, Retval retval) +# __semwait_signal_nocancel(Int32 cond_sem, Int32 mutex_sem, Int32 timeout, Int32 relative, int64_t tv_sec, int32_t tv_nsec, Retval retval) +# __mac_mount(char *type, Buffer path, Int32 flags, Nullptr data, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_get_mount(Buffer path, RandomStruct[struct=mac] mac_p, Retval retval) +# __mac_getfsstat(user_addr_t buf, Int32 bufsize, user_addr_t mac, Int32 macsize, Int32 flags, Retval retval) +# fsgetpath(user_addr_t buf, size_t bufsize, user_addr_t fsid, UInt64 objid, Ptr[field=Declared[c_type="unsigned long"]] retval) +# audit_session_self(mach_port_name_t *retval) +# audit_session_join(mach_port_name_t port, Retval retval) +# sys_fileport_makeport(Int32 fd, user_addr_t portnamep, Retval retval) +# sys_fileport_makefd(mach_port_name_t port, Retval retval) +# audit_session_port(au_asUInt32 asid, user_addr_t portnamep, Retval retval) +# pid_suspend(Int32 pid, Retval retval) +# pid_resume(Int32 pid, Retval retval) +# pid_shutdown_sockets(Int32 pid, Int32 level, Retval retval) +# shared_region_map_and_slide_np(Int32 fd, UInt32 count, RandomStruct[struct=shared_file_mapping_np] mappings, UInt32 slide, uint64_t*slide_start, UInt32 slide_size, Retval retval) +# kas_info(Int32 selector, void *value, size_t *size, Retval retval) +# memorystatus_control(UInt32 command, int32_t pid, UInt32 flags, user_addr_t buffer, size_t CappedSize, Retval retval) +# guarded_open_np(Buffer path, guardPtr[field=Declared[c_type=int]] guard, UInt32 guardflags, Int32 flags, Int32 mode, Retval retval) +# guarded_close_np(Int32 fd, guardPtr[field=Declared[c_type=int]] guard, Retval retval) +# guarded_kqueue_np(guardPtr[field=Declared[c_type=int]] guard, UInt32 guardflags, Retval retval) +# change_fdguard_np(Int32 fd, guardPtr[field=Declared[c_type=int]] guard, UInt32 guardflags, guardPtr[field=Declared[c_type=int]] nguard, UInt32 nguardflags, Ptr[field=Declared[c_type=int]] fdflagsp, Retval retval) +# usrctl(UInt32 flags, Retval retval) +# proc_rlimit_control(Int32 pid, Int32 flavor, void *arg, Retval retval) +# connectx(Int32 socket, sa_endpoints_t *endpoints, Enum[proto_type=SaeAssocID] associd, Int32 flags, RandomStruct[struct=iovec] iov, Int32 iovcnt, size_t *len, sae_connPtr[field=Declared[c_type=int]] connid, Retval retval) +# disconnectx(FileDescriptor s, Enum[proto_type=SaeAssocID] aid, UInt32 cid, Retval retval) +peeloff(FileDescriptor s, Enum[proto_type=SaeAssocID] aid, Retval retval) +socket_delegate(Enum[proto_type=Domain] domain, Enum[proto_type=SocketType] type, Enum[proto_type=Protocol] protocol, Int32 epid, Retval retval) +# telemetry(UInt64 cmd, UInt64 deadline, UInt64 interval, UInt64 leeway, UInt64 arg4, UInt64 arg5, Retval retval) +# proc_uuid_policy(UInt32 operation, uUInt32 uuid, size_t uuidlen, UInt32 flags, Retval retval) +# memorystatus_get_level(user_addr_t level, Retval retval) +# system_override(UInt64 timeout, UInt64 flags, Retval retval) +# vfs_purge(Retval retval) +# sfi_ctl(UInt32 operation, UInt32 sfi_class, UInt64 time, Ptr[field=Declared[c_type="unsigned long"]] out_time, Retval retval) +# sfi_pidctl(UInt32 operation, Int32 pid, UInt32 sfi_flags, Ptr[field=Declared[c_type="unsigned int"]] out_sfi_flags, Retval retval) +# coalition(UInt32 operation, Ptr[field=Declared[c_type="unsigned long"]] cid, UInt32 flags, Retval retval) +# coalition_info(UInt32 flavor, Ptr[field=Declared[c_type="unsigned long"]] cid, void *buffer, size_t *bufsize, Retval retval) +necp_match_policy(EmptyBuffer[cast="uint8_t *", size_given_by=parameters_size] parameters, CappedSize parameters_size, RandomStruct[struct=necp_aggregate_result] returned_result, Retval retval) +# getattrlistbulk(Int32 dirfd, RandomStruct[struct=attrlist] alist, void *attributeBuffer, size_t CappedSize, UInt64 options, Retval retval) +# clonefileat(Int32 src_dirfd, user_addr_t src, Int32 dst_dirfd, user_addr_t dst, UInt32 flags, Retval retval) +# openat(Int32 fd, Buffer path, Int32 flags, Int32 mode, Retval retval) +# openat_nocancel(Int32 fd, Buffer path, Int32 flags, Int32 mode, Retval retval) +# renameat(Int32 fromfd, char *from, Int32 tofd, char *to, Retval retval) +# faccessat(Int32 fd, Buffer path, Int32 amode, Int32 flag, Retval retval) +# fchmodat(Int32 fd, Buffer path, Int32 mode, Int32 flag, Retval retval) +# fchownat(Int32 fd, Buffer path, UInt32 uid, UInt32 gid, Int32 flag, Retval retval) +# fstatat(Int32 fd, Buffer path, user_addr_t ub, Int32 flag, Retval retval) +# fstatat64(Int32 fd, Buffer path, user_addr_t ub, Int32 flag, Retval retval) +# linkat(Int32 fd1, Buffer path, Int32 fd2, user_addr_t link, Int32 flag, Retval retval) +# unlinkat(Int32 fd, Buffer path, Int32 flag, Retval retval) +# readlinkat(Int32 fd, Buffer path, user_addr_t buf, size_t bufsize, Retval retval) +# symlinkat(user_addr_t *path1, Int32 fd, Buffer path2, Retval retval) +# mkdirat(Int32 fd, Buffer path, Int32 mode, Retval retval) +# getattrlistat(Int32 fd, Buffer path, RandomStruct[struct=attrlist] alist, void *attributeBuffer, size_t CappedSize, UInt64 options, Retval retval) +# proc_trace_log(Int32 pid, UInt64 uniqueid, Retval retval) +# bsdthread_ctl(user_addr_t cmd, user_addr_t arg1, user_addr_t arg2, user_addr_t arg3, Retval retval) +# openbyid_np(user_addr_t fsid, user_addr_t objid, Int32 oflags, Retval retval) +# recvmsg_x(Int32 s, RandomStruct[struct=msghdr_x] msgp, UInt32 cnt, Int32 flags, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sendmsg_x(Int32 s, RandomStruct[struct=msghdr_x] msgp, UInt32 cnt, Int32 flags, Ptr[field=Declared[c_type="unsigned long"]] retval) +# thread_selfusage(Ptr[field=Declared[c_type="unsigned long"]] retval) +# csrctl(UInt32 op, user_addr_t useraddr, user_addr_t usersize, Retval retval) +# guarded_open_dprotected_np(Buffer path, guardPtr[field=Declared[c_type=int]] guard, UInt32 guardflags, Int32 flags, Int32 dpclass, Int32 dpflags, Int32 mode, Retval retval) +# guarded_write_np(Int32 fd, guardPtr[field=Declared[c_type=int]] guard, user_addr_t cbuf, user_size_t nbyte, Ptr[field=Declared[c_type="unsigned long"]] retval) +# guarded_pwrite_np(Int32 fd, guardPtr[field=Declared[c_type=int]] guard, user_addr_t buf, user_size_t nbyte, UInt64 offset, Ptr[field=Declared[c_type="unsigned long"]] retval) +# guarded_writev_np(Int32 fd, guardPtr[field=Declared[c_type=int]] guard, RandomStruct[struct=iovec] iovp, Int32 iovcnt, Ptr[field=Declared[c_type="unsigned long"]] retval) +# renameatx_np(Int32 fromfd, char *from, Int32 tofd, char *to, UInt32 flags, Retval retval) +# mremap_encrypted(Nullptr addr, size_t len, UInt32 cryptid, UInt32 cputype, UInt32 cpusubtype, Retval retval) +# stack_snapshot_with_config(Int32 stackshot_config_version, user_addr_t stackshot_config, size_t stackshot_config_size, Retval retval) +# microstackshot(user_addr_t tracebuf, UInt32 tracebuf_size, UInt32 flags, Retval retval) +# grab_pgo_data(user_addr_t uuid, Int32 flags, user_addr_t buffer, user_ssize_t size, Ptr[field=Declared[c_type="unsigned long"]] retval) +# persona(UInt32 operation, UInt32 flags, RandomStruct[struct=kpersona_info] info, Ptr[field=Declared[c_type=int]] id, size_t *idlen, Buffer path, Retval retval) +# mach_eventlink_signal(mach_port_name_t eventlink_port, UInt64 signal_count, Ptr[field=Declared[c_type="unsigned long"]] retval) +# mach_eventlink_wait_until(mach_port_name_t eventlink_port, UInt64 wait_count, UInt64 deadline, UInt32 clock_id, UInt32 option, Ptr[field=Declared[c_type="unsigned long"]] retval) +# mach_eventlink_signal_wait_until(mach_port_name_t eventlink_port, UInt64 wait_count, UInt64 signal_count, UInt64 deadline, UInt32 clock_id, UInt32 option, Ptr[field=Declared[c_type="unsigned long"]] retval) +# work_interval_ctl(UInt32 operation, UInt64 work_interval_id, void *arg, size_t len, Retval retval) +# getentropy(void *buffer, size_t size, Retval retval) +necp_open(Flags[proto_type=NecpOpenFlag] flags, Retval retval) +# necp_client_action(Int32 necp_fd, UInt32 action, uUInt32 client_id, size_t client_id_len, uint8_t *buffer, size_t buffer_size, Retval retval) +# sys_ulock_wait(UInt32 operation, void *addr, UInt64 value, UInt32 timeout, Retval retval) +# sys_ulock_wake(UInt32 operation, void *addr, UInt64 wake_value, Retval retval) +# fclonefileat(Int32 src_fd, Int32 dst_dirfd, user_addr_t dst, UInt32 flags, Retval retval) +# fs_snapshot(UInt32 op, Int32 dirfd, user_addr_t name1, user_addr_t name2, user_addr_t data, UInt32 flags, Retval retval) +# terminate_with_payload(Int32 pid, UInt32 reason_namespace, UInt64 reason_code, void *payload, UInt32 payload_size, char *reason_string, UInt64 reason_flags, Retval retval) +# abort_with_payload(UInt32 reason_namespace, UInt64 reason_code, void *payload, UInt32 payload_size, char *reason_string, UInt64 reason_flags, void *retval) +necp_session_open(Int32 flags, Retval retval) +# necp_session_action(Int32 necp_fd, UInt32 action, uint8_t *in_buffer, size_t in_buffer_length, uint8_t *out_buffer, size_t out_buffer_length, Retval retval) +# setattrlistat(Int32 fd, Buffer path, RandomStruct[struct=attrlist] alist, void *attributeBuffer, size_t CappedSize, UInt32 options, Retval retval) +# net_qos_guideline(RandomStruct[struct=net_qos_param] param, UInt32 param_len, Retval retval) +# fmount(char *type, Int32 fd, Int32 flags, void *data, Retval retval) +# ntp_adjtime(RandomStruct[struct=timex] tp, Retval retval) +# ntp_gettime(RandomStruct[struct=ntptimeval] ntvp, Retval retval) +# os_fault_with_payload(UInt32 reason_namespace, UInt64 reason_code, void *payload, UInt32 payload_size, char *reason_string, UInt64 reason_flags, Retval retval) +# kqueue_workloop_ctl(user_addr_t cmd, UInt64 options, user_addr_t addr, size_t sz, Retval retval) +# __mach_bridge_remote_time(UInt64 local_timestamp, Ptr[field=Declared[c_type="unsigned long"]] retval) +# coalition_ledger(UInt32 operation, Ptr[field=Declared[c_type="unsigned long"]] cid, void *buffer, size_t *bufsize, Retval retval) +# log_data(Int32 tag, Int32 flags, void *buffer, Int32 size, Retval retval) +# memorystatus_available_memory(Ptr[field=Declared[c_type="unsigned long"]] retval) +# shared_region_map_and_slide_2_np(UInt32 files_count, RandomStruct[struct=shared_file_np] files, UInt32 mappings_count, RandomStruct[struct=shared_file_mapping_slide_np] mappings, Retval retval) +# pivot_root(char *new_rootfs_path_before, char *old_rootfs_path_after, Retval retval) +# task_inspect_for_pid(mach_port_name_t target_tport, Int32 pid, mach_port_name_t *t, Retval retval) +# task_read_for_pid(mach_port_name_t target_tport, Int32 pid, mach_port_name_t *t, Retval retval) +# sys_preadv(Int32 fd, RandomStruct[struct=iovec] iovp, Int32 iovcnt, UInt64 offset, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sys_pwritev(Int32 fd, RandomStruct[struct=iovec] iovp, Int32 iovcnt, UInt64 offset, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sys_preadv_nocancel(Int32 fd, RandomStruct[struct=iovec] iovp, Int32 iovcnt, UInt64 offset, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sys_pwritev_nocancel(Int32 fd, RandomStruct[struct=iovec] iovp, Int32 iovcnt, UInt64 offset, Ptr[field=Declared[c_type="unsigned long"]] retval) +# sys_ulock_wait2(UInt32 operation, void *addr, UInt64 value, UInt64 timeout, UInt64 value2, Retval retval) +# proc_info_extended_id(int32_t callnum, int32_t pid, UInt32 flavor, UInt32 flags, UInt64 ext_id, UInt64 arg, user_addr_t buffer, int32_t CappedSize, Retval retval) +# tracker_action(Int32 action, char *buffer, size_t buffer_size, Retval retval) +# debug_syscall_reject(UInt64 packed_selectors, Retval retval) +# freadlink(Int32 fd, user_addr_t buf, user_size_t bufsize, Retval retval) diff --git a/tools/generate_syscall/downstream_field.py b/tools/generate_syscall/downstream_field.py new file mode 100644 index 0000000..cbd63a1 --- /dev/null +++ b/tools/generate_syscall/downstream_field.py @@ -0,0 +1,424 @@ +# Copyright 2024 Google LLC +# +# 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. + +class Field: + name: str | None + cast: str | None + max_value: int | None + + def __init__(self, name=None, cast=None, max_value=None): + assert name is not None + self.name = name + self.cast = cast + self.max_value = max_value + + @property + def command_field(self): + # Helper for codegen. + if self.max_value is not None: + return f"(args.{self.name}() % {self.max_value})" + return f"args.{self.name}()" + + def has_dependency(self): + # Whether this field depends on another field. + return False + + def as_caller_setup(self, syscall): + # Inserted before wrapper call. + return "" + + def as_caller_arg(self, syscall): + # Inserted as argument in wrapper call. + return self.command_field + + def as_caller_cleanup(self, syscall): + # Inserted after wrapper call. + return "" + + def as_value(self, syscall): + # When the value is needed, and the caller_arg + # might need a dereference. + return self.as_caller_arg(syscall) + + def as_size_of(self, syscall): + # Size of this field, if defined. + return self.as_value(syscall) + + def as_proto_message_type(self, syscall): + # Inserted into the syscall protobuf message. + # Only include the type, like 'required uint64'. + # The name and number are added automatically. + # Empty string means to leave out of the protobuf. + return "" + + +class Value(Field): + # syscall value + # a value, given by or deduced from protobuf + proto_type: str | None + c_type: str | None + + def __init__(self, proto_type=None, c_type=None, **kwargs): + super().__init__(**kwargs) + self.proto_type = proto_type + self.c_type = c_type + + def as_caller_setup(self, syscall): + return f"{self.c_type} {self.name} = {self.command_field};\n" + + def as_caller_arg(self, syscall): + return self.name + + def as_proto_message_type(self, syscall): + return f"required {self.proto_type}" + + +class Bool(Value): + def __init__(self, **kwargs): + super().__init__("bool", "int", **kwargs) + + +class UInt32(Value): + def __init__(self, **kwargs): + super().__init__("uint32", "unsigned int", **kwargs) + + +class Int32(Value): + def __init__(self, **kwargs): + super().__init__("int32", "int", **kwargs) + + def __repr__(self): + return f"Int32({self.name})" + + +class UInt64(Value): + def __init__(self, **kwargs): + super().__init__("uint64", "unsigned long", **kwargs) + + +class Int64(Value): + def __init__(self, **kwargs): + super().__init__("int64", "long", **kwargs) + + +class Declared(Field): + # uninitialized stack variable + c_type: str | None + + def __init__(self, c_type=None, **kwargs): + super().__init__(**kwargs) + assert isinstance(c_type, str) + self.c_type = c_type + + def __repr__(self): + return f"Declared({self.name})" + + def as_caller_setup(self, syscall): + return f"{self.c_type} {self.name};\n" + + def as_caller_arg(self, syscall): + return self.name + + +class Ptr(Field): + # prepend & to caller_arg + # TODO(nwach): these kinds of types are annoying to write... + field: Field + + def __init__(self, field=None, **kwargs): + super().__init__(**kwargs) + assert field is not None + self.field = field + self.max_value = self.field.max_value + + def as_caller_setup(self, syscall): + return self.field.as_caller_setup(syscall) + + def as_caller_arg(self, syscall): + return f"&({self.field.as_caller_arg(syscall)})" + + def as_caller_cleanup(self, syscall): + return self.field.as_caller_cleanup(syscall) + + def as_value(self, syscall): + return self.field.as_value(syscall) + + def as_size_of(self, syscall): + return self.field.as_size_of(syscall) + + def as_proto_message_type(self, syscall): + return self.field.as_proto_message_type(syscall) + + +class Retval(Ptr): + def __init__(self, **kwargs): + super().__init__(Declared(name=kwargs.get('name'), c_type='int'), **kwargs) + + +class CappedSize(UInt64): + # provides the size of an empty array + # anything with a max_value can be a buffer size + # this is just a utility class + def __init__(self, **kwargs): + super().__init__(max_value=4096, **kwargs) + + +class EmptyArray(Field): + size_given_by: str | None + fixed_size: str | None + c_type: str | None + + def __init__(self, size_given_by=None, fixed_size=None, c_type=None, **kwargs): + super().__init__(**kwargs) + self.size_given_by = size_given_by + self.fixed_size = fixed_size + self.c_type = c_type + + def has_dependency(self): + # Size given by another field + return self.size_given_by is not None + + def as_caller_arg(self, syscall): + return f"{self.name}.data()" + + def as_caller_setup(self, syscall): + if self.size_given_by is not None: + size_field = syscall.field_by_name(self.size_given_by) + if size_field.max_value is None: + raise ValueError( + f"Must define max_value for size field '{size_field.name}' used by '{self.name}' in '{syscall.name}'" + ) + size = size_field.as_value(syscall) + elif self.fixed_size is not None: + size = self.fixed_size + else: + raise ValueError(f"No way to size '{self.name}' in '{syscall.name}'") + + return f"std::vector<{self.c_type}> {self.name}({size});\n" + + +class EmptyBuffer(EmptyArray): + # arrays of char are common enough to warrant own type + def __init__(self, **kwargs): + super().__init__(c_type="char", **kwargs) + + +class Array(Field): + # of_type must be a Value, specifying c_type and proto_type + # TODO(nwach): this isn't even close to working for more complicated types + of_type: Value | None + + def __init__(self, of_type=None, **kwargs): + super().__init__(**kwargs) + assert False + # self.of_type = self.parse_arg_as_type(of_type) + + def as_caller_setup(self, syscall): + # a bit hacky... + assert self.of_type is not None + out = f"std::vector<{self.of_type.c_type}> {self.name};\n" + out += f"for(auto c : {self.command_field})" + "{\n" + out += f" {self.name}.push_back(c);\n" + out += "}\n" + return out + + def as_caller_arg(self, syscall): + assert self.name is not None + return self.name + ".data()" + + def as_size_of(self, syscall): + assert self.name is not None + return self.name + ".size()" + + def as_proto_message_type(self, syscall): + assert self.of_type is not None + assert self.of_type.proto_type is not None + return "repeated" + self.of_type.proto_type + + +class Buffer(Field): + # a buffer is like an array, but much simpler cause proto bytes scalar + def as_caller_arg(self, syscall): + return "(user_addr_t) " + self.command_field + ".data()" + + def as_proto_message_type(self, syscall): + return f"required bytes" + + def as_size_of(self, syscall): + return self.command_field + ".size()" + + +class SizeOf(Field): + # resolves to the size of an container (usually array) + container: str | None + + def __init__(self, container=None, **kwargs): + super().__init__(**kwargs) + self.container = container + + def as_caller_arg(self, syscall): + container = syscall.field_by_name(self.container) + return container.as_size_of(syscall) + + +class Literal(Field): + value: str | None + + def __init__(self, value=None, **kwargs): + super().__init__(**kwargs) + self.value = value + + def as_caller_arg(self, syscall): + return self.value + + +class Nullptr(Literal): + def __init__(self, **kwargs): + super().__init__("(user_addr_t) nullptr", **kwargs) + + +class Enum(Value): + # enum with given name should be defined somewhere... + def __init__(self, proto_type=None, **kwargs): + if 'c_type' in kwargs: + raise ValueError("The 'c_type' argument is not supported in the Enum class. " + "Did you mean to use 'proto_type' instead?") + kwargs['c_type'] = "int" + super().__init__(proto_type=proto_type, **kwargs) + + +class Flags(Enum): + c_type: str | None + + def __init__(self, proto_type=None, c_type='int', **kwargs): + if proto_type is None: + raise ValueError("The 'proto_type' argument is required in the Flags class.") + if 'c_type' in kwargs: + raise ValueError("The 'c_type' argument is not supported in the Flags class. " + "Did you mean to use 'proto_type' instead?") + super().__init__(proto_type=proto_type, **kwargs) + self.c_type = c_type + + def as_caller_setup(self, syscall): + # enums are int32 + out = f"{self.c_type} {self.name} = 0;" + out += f"for(auto f : {self.command_field}) " + "{\n" + out += f" {self.name} |= ({self.c_type}) f;\n" + out += "}\n" + return out + + def as_proto_message_type(self, syscall): + assert self.proto_type is not None + return "repeated " + self.proto_type + + def as_caller_arg(self, syscall): + return self.name + + +class RandomStruct(Field): + # A struct (or any c type), filled with up to sizeof(struct) fuzzed bytes + struct: str | None + + def __init__(self, struct=None, **kwargs): + super().__init__(**kwargs) + self.struct = struct + + def as_proto_message_type(self, syscall): + return "required bytes" + + def as_caller_setup(self, syscall): + size = f"std::min({self.command_field}.size(), sizeof(struct {self.struct}))" + out = f"struct {self.struct} {self.name};\n" + out += f"memcpy(&{self.name}, {self.command_field}.data(), {size});\n" + return out + + def as_caller_arg(self, syscall): + assert self.name is not None + return "&" + self.name + + +""" +CUSTOM TYPES +""" + + +class SockAddr(Field): + # good example of custom type + # uses already defined proto message + # uses helper func get_sockaddr defined in bsd.h/cc + def as_proto_message_type(self, syscall): + return "required SockAddr " + + def as_caller_setup(self, syscall): + return f"std::string {self.name} = get_sockaddr({self.command_field});\n" + + def as_caller_arg(self, syscall): + return f"(caddr_t) {self.name}.data()" + + def as_size_of(self, syscall): + return f"{self.name}.size()" + + +class RLimit(Field): + # ok example of custom type + # uses already defined proto message + # manually sets up struct + def as_proto_message_type(self, syscall): + return "required RLimit " + + def as_caller_setup(self, syscall): + out = f"struct rlimit {self.name} " + "{\n" + out += f" .rlim_cur = {self.command_field}.current(),\n" + out += f" .rlim_max = {self.command_field}.max(),\n" + out += "};\n" + return out + + def as_caller_arg(self, syscall): + return f"&{self.name}" + + +class UUID(EmptyArray): + def __init__(self, **kwargs): + super().__init__(c_type="unsigned char", fixed_size=16, **kwargs) + + +class EmptySocketPair(EmptyArray): + def __init__(self, **kwargs): + super().__init__(c_type="int", fixed_size=2, **kwargs) + + +class EmptyPipeFds(EmptyArray): + def __init__(self, **kwargs): + super().__init__(c_type="int", fixed_size=2, **kwargs) + + +class Socklen(Value): + def __init__(self, **kwargs): + super().__init__( + c_type="socklen_t", proto_type="uint32", max_value=4096, **kwargs + ) + + +class FileDescriptor(Enum): + def __init__(self, **kwargs): + super().__init__("FileDescriptor", **kwargs) + + +def all_subclasses(cls): + return set(cls.__subclasses__()).union( + [s for c in cls.__subclasses__() for s in all_subclasses(c)] + ) + + +FIELD_CLASSES = {cls.__name__: cls for cls in all_subclasses(Field)} diff --git a/tools/generate_syscall/downstream_syscall.py b/tools/generate_syscall/downstream_syscall.py new file mode 100644 index 0000000..fcabc87 --- /dev/null +++ b/tools/generate_syscall/downstream_syscall.py @@ -0,0 +1,118 @@ +# Copyright 2024 Google LLC +# +# 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. + +import re +import textwrap +from typing import List +from sockfuzzer.tools.generate_syscall.downstream_field import Field +from sockfuzzer.tools.generate_syscall.downstream_syscall_parser import parse_syscall + + +class DownstreamSyscall: + name: str + fields: List[Field] + + def __init__(self, name, fields): + self.name = name + self.fields = fields + + @classmethod + def from_line(cls, line): + name, fields = parse_syscall(line) + return cls(name, fields) + + def field_by_name(self, name): + for field in self.fields: + if field.name == name: + return field + raise KeyError("Field not found:", name) + + @property + def capitalized_name(self): + words = self.name.replace("_", " ").split() + return "".join(word.capitalize() for word in words) + + @property + def wrapper_name(self): + return self.name + "_wrapper" + + @property + def proto_message_name(self): + return self.capitalized_name + "Message" + + @property + def proto_message_body(self): + body = "" + i = 1 + for field in self.fields: + entry = field.as_proto_message_type(self) + if len(entry) == 0: + continue + body += f"{entry} {field.name} = {i};\n" + i += 1 + return body + + @property + def proto_message(self): + message = "message " + self.proto_message_name + " {\n" + message += textwrap.indent(self.proto_message_body, " ") + message += "}\n" + return message + + @property + def proto_syscall_field(self): + return self.proto_message_name + " " + self.name + + @property + def caller_case(self): + return "case BsdSyscall::k" + self.capitalized_name + ": {\n" + + @property + def caller_args(self): + args = [] + for field in self.fields: + cast = "" + if field.cast is not None: + cast = f"({field.cast})" + args.append(cast + field.as_caller_arg(self)) + return ", ".join(args) + + @property + def caller_call(self): + return self.wrapper_name + "(" + self.caller_args + ");\n" + + @property + def caller_case_block(self): + out = "auto args = syscall." + self.name + "();\n" + + for field in reversed(self.fields): + if not field.has_dependency(): + out += field.as_caller_setup(self) + + for field in reversed(self.fields): + if field.has_dependency(): + out += field.as_caller_setup(self) + + out += self.caller_call + for field in self.fields: + out += field.as_caller_cleanup(self) + out += "break;\n" + return out + + @property + def calling_code(self): + out = self.caller_case + out += textwrap.indent(self.caller_case_block, " ") + out += "}\n" + return out diff --git a/tools/generate_syscall/downstream_syscall_manager.py b/tools/generate_syscall/downstream_syscall_manager.py new file mode 100644 index 0000000..57cf48a --- /dev/null +++ b/tools/generate_syscall/downstream_syscall_manager.py @@ -0,0 +1,197 @@ +# Copyright 2024 Google LLC +# +# 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. + +""" +Codegen is messy. And it's made messier by the number of edge cases and +exceptions in how bsd syscalls are named/typed/formatted/etc. + +generate_wrappers.py takes care of a few edge cases. At this point, we +need to generate protobufs for each syscall, and code that converts the +protobuf into arguments and calls the syscall wrapper. That's what happens +in this file. + +Syscalls do not have meaningful type information. We require manually +typing the arguments in syscalls.defs. This uses a custom grammar +(somewhat inspired by syzlang). Types are defined in syscall_fields.py. + +To add more syscalls, you will hopefully only need to touch syscalls.defs +and syscall_fields.py. +""" +import textwrap +import io +import sys + +from tools.generate_syscall.downstream_syscall import DownstreamSyscall +from tools.generate_syscall.upstream_syscall_manager import UpstreamSyscallManager + + +class DownstreamSyscallManager: + """ + Container for all syscalls + """ + + def __init__(self, syscalls): + self.syscalls = syscalls + + @classmethod + def from_defs(cls, path): + syscalls = [] + with open(path, "r") as syscall_defs: + for line in syscall_defs: + line = line.strip() + if len(line) == 0 or line.startswith("#"): + continue + syscall = DownstreamSyscall.from_line(line) + syscalls.append(syscall) + print(f"Parsed {len(syscalls)} syscalls from {path}") + print("Parsed syscalls:", [syscall.name for syscall in syscalls]) + return cls(syscalls) + + def write_protos(self, upstream_syscall_manager, out: io.TextIOBase): + preamble = """\ + // Autogenerated by generate.sh + + syntax = "proto2"; + + import "fuzz/proto/bsd_types.proto"; + import "fuzz/proto/bsd_enums.proto"; + + """ + fields = "" + output = textwrap.dedent(preamble) + + upstream_syscalls = {} + missing_syscalls = [] + for syscall in self.syscalls: + upstream_syscall = upstream_syscall_manager.find(syscall.name) + if upstream_syscall is None: + missing_syscalls.append(syscall.name) + else: + upstream_syscalls[syscall.name] = upstream_syscall + + if missing_syscalls: + print(f"\nError: {len(missing_syscalls)} syscalls not found in upstream_syscalls:") + for name in missing_syscalls: + print(f" {name}") + print(f"\nTotal downstream syscalls: {len(self.syscalls)}") + print(f"Total upstream syscalls: {len(upstream_syscall_manager.syscalls)}") + raise ValueError(f"Couldn't find {len(missing_syscalls)} syscalls in upstream_syscalls") + + syscalls = sorted( + self.syscalls, key=lambda syscall: upstream_syscalls[syscall.name].number + ) + for i, syscall in enumerate(syscalls): + try: + output += syscall.proto_message + "\n" + # TODO(nedwill): set i using syscalls.name + fields += f"{syscall.proto_syscall_field} = {upstream_syscalls[syscall.name].number};" + if i + 1 < len(syscalls): + fields += "\n" + except Exception as exc: + raise ValueError( + f"Error while generating protos of '{syscall.name}'" + ) from exc + + fields = textwrap.indent(fields, " ") + syscalls = """\ + message BsdSyscall {{ + oneof syscall {{\n{fields} + }} + }} + """.format( + fields=fields + ) + output += textwrap.dedent(syscalls) + + out.write(output) + + def write_caller(self, out): + preamble = """\ + // Autogenerated by generate.sh + + #include "tools/generate_syscall/call_bsd_syscall.h" + + #include "fuzz/handlers/bsd_types.h" + #include "tools/generate_syscall/syscall_wrappers_generated.h" + + void CallBsdSyscall(const BsdSyscall &syscall) { + switch (syscall.syscall_case()) { + """ + output = textwrap.dedent(preamble) + for syscall in self.syscalls: + try: + output += textwrap.indent(syscall.calling_code, " ") + except: + raise ValueError(f"Error while generating caller of '{syscall.name}'") + output += "default:\n" + output += " break;" + output += " }\n}" + out.write(output) + + +if __name__ == "__main__": + + import argparse + + parser = argparse.ArgumentParser(description="Codegen based on syscalls.defs") + parser.add_argument( + "-i", + dest="input", + required=True, + metavar="FILE", + help="Path to syscalls.defs", + ) + parser.add_argument( + "-u", + dest="upstream_input", + required=True, + metavar="FILE", + help="Path to syscalls.master", + ) + parser.add_argument( + "-D", + dest="flags", + action="append", + help="Define a compiler flag for filtering syscalls.", + ) + parser.add_argument( + "--protos", dest="protos", metavar="FILE", help="Path to save protos" + ) + parser.add_argument( + "--caller", + dest="caller", + metavar="FILE", + help="Path to save calling code", + ) + args = parser.parse_args() + + upstream_syscalls = UpstreamSyscallManager.from_path( + args.upstream_input, args.flags, [] + ) + if upstream_syscalls is None: + print(f"Failed to parse {args.upstream_input}") + sys.exit(1) + + syscalls = DownstreamSyscallManager.from_defs(args.input) + if syscalls is None: + print(f"Failed to parse {args.input}") + sys.exit(1) + + if args.protos: + with open(args.protos, "w") as out: + syscalls.write_protos(upstream_syscalls, out) + + if args.caller: + with open(args.caller, "w") as out: + syscalls.write_caller(out) diff --git a/tools/generate_syscall/downstream_syscall_manager_test.py b/tools/generate_syscall/downstream_syscall_manager_test.py new file mode 100644 index 0000000..77c13f5 --- /dev/null +++ b/tools/generate_syscall/downstream_syscall_manager_test.py @@ -0,0 +1,108 @@ +# Copyright 2024 Google LLC +# +# 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. + +import io +import unittest + +from sockfuzzer.tools.generate_syscall.downstream_syscall_manager import DownstreamSyscallManager +from sockfuzzer.tools.generate_syscall.downstream_syscall import DownstreamSyscall +from sockfuzzer.tools.generate_syscall.upstream_syscall import UpstreamSyscall +from sockfuzzer.tools.generate_syscall.upstream_syscall_manager import UpstreamSyscallManager + +SENDMSG_DEF = "sendmsg(FileDescriptor s, RandomStruct[cast=caddr_t, struct=msghdr] msg, Flags[proto_type=MsgFlags] flags, Retval retval)" +SENDMSG_UPSTREAM = ( + "28 AUE_SENDMSG ALL { int sendmsg(int s, caddr_t msg, int flags) NO_SYSCALL_STUB; }" +) +ACCEPT_DEF = "accept(FileDescriptor s, EmptyBuffer[cast=caddr_t, size_given_by=anamelen] name, Ptr[field=Socklen] anamelen, Retval retval)" +ACCEPT_UPSTREAM = "30 AUE_ACCEPT ALL { int accept(int s, caddr_t name, socklen_t *anamelen) NO_SYSCALL_STUB; }" + +TWO_PROTOS = """// Autogenerated by generate.sh + +syntax = "proto2"; + +import "fuzz/proto/bsd_types.proto"; +import "fuzz/proto/bsd_enums.proto"; + +message SendmsgMessage { + required FileDescriptor s = 1; + required bytes msg = 2; + repeated MsgFlags flags = 3; +} + +message AcceptMessage { + required FileDescriptor s = 1; + required uint32 anamelen = 2; +} + +message BsdSyscall { + oneof syscall { + SendmsgMessage sendmsg = 28; + AcceptMessage accept = 30; + } +} +""" + + +class ProtobufTest(unittest.TestCase): + # Allow larger diffs for this test + maxDiff = None + + def test_write_protos_correct_number(self): + sendmsg_syscall = DownstreamSyscall.from_line(SENDMSG_DEF) + accept_syscall = DownstreamSyscall.from_line(ACCEPT_DEF) + + sendmsg_upstream = UpstreamSyscall.from_line(SENDMSG_UPSTREAM, None, None) + accept_upstream = UpstreamSyscall.from_line(ACCEPT_UPSTREAM, None, None) + upstream_syscall_manager = UpstreamSyscallManager( + [sendmsg_upstream, accept_upstream] + ) + + syscalls = DownstreamSyscallManager([sendmsg_syscall, accept_syscall]) + buffer = io.StringIO() + syscalls.write_protos(upstream_syscall_manager, buffer) + self.assertEqual(buffer.getvalue(), TWO_PROTOS) + + def test_write_protos_correct_number_different_order(self): + """ + Order of downstream syscalls should not affect the ordering in + the generated protobuf file. + """ + sendmsg_syscall = DownstreamSyscall.from_line(SENDMSG_DEF) + accept_syscall = DownstreamSyscall.from_line(ACCEPT_DEF) + + sendmsg_upstream = UpstreamSyscall.from_line(SENDMSG_UPSTREAM, None, None) + accept_upstream = UpstreamSyscall.from_line(ACCEPT_UPSTREAM, None, None) + upstream_syscall_manager = UpstreamSyscallManager( + [sendmsg_upstream, accept_upstream] + ) + + # Opposite order should yield the same result + syscalls = DownstreamSyscallManager([accept_syscall, sendmsg_syscall]) + buffer = io.StringIO() + syscalls.write_protos(upstream_syscall_manager, buffer) + self.assertEqual(buffer.getvalue(), TWO_PROTOS) + + def test_write_protos_unknown_syscall(self): + dummy_syscall = DownstreamSyscall.from_line( + "dummy(Literal[cast=my_cast_t, value=0] val)" + ) + upstream_syscall_manager = UpstreamSyscallManager([]) + syscalls = DownstreamSyscallManager([dummy_syscall]) + buffer = io.StringIO() + with self.assertRaises(ValueError): + syscalls.write_protos(upstream_syscall_manager, buffer) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/generate_syscall/downstream_syscall_parser.py b/tools/generate_syscall/downstream_syscall_parser.py new file mode 100644 index 0000000..f81ccba --- /dev/null +++ b/tools/generate_syscall/downstream_syscall_parser.py @@ -0,0 +1,257 @@ +# Copyright 2024 Google LLC +# +# 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. + +from __future__ import annotations +import re +from typing import List, Tuple, Union +from sockfuzzer.tools.generate_syscall.downstream_field import FIELD_CLASSES, Field + +# ::= ( ) +# ::= | , +# ::= +# ::= | [ ] +# ::= any key in FIELD_CLASSES +# ::= | , +# ::= = +# ::= | | " " + + +class BasicType: + def __init__(self, name: str): + self.name = name + + def __eq__(self, other): + return self.name == other.name + + def __repr__(self): + return f"BasicType({self.name!r})" + + def __str__(self): + return self.name + + +class TypeArg: + def __init__(self, attr_identifier: str, value: str | int | Type): + self.attr_identifier = attr_identifier + self.value = value + + def __eq__(self, other): + return ( + self.attr_identifier == other.attr_identifier and self.value == other.value + ) + + def __repr__(self): + return f"TypeArg({self.attr_identifier!r}, {self.value!r})" + + def __str__(self): + return f"{self.attr_identifier}={self.value}" + + +class Type: + def __init__(self, basic_type: BasicType, type_args: List[TypeArg]): + self.basic_type = basic_type + self.type_args = type_args + + def __eq__(self, other): + return self.basic_type == other.basic_type and self.type_args == other.type_args + + def __repr__(self): + return f"Type({self.basic_type!r}, {self.type_args!r})" + + def __str__(self): + type_args_str = ( + f"[{', '.join(str(arg) for arg in self.type_args)}]" + if self.type_args + else "" + ) + return f"{self.basic_type}{type_args_str}" + + def as_field(self, name=None) -> Field: + field_cls = FIELD_CLASSES[self.basic_type.name] + kwargs = {} + for type_arg in self.type_args: + if isinstance(type_arg.value, Type): + kwargs[type_arg.attr_identifier] = type_arg.value.as_field(name) + else: + kwargs[type_arg.attr_identifier] = type_arg.value + kwargs["name"] = name + return field_cls(**kwargs) + +class Argument: + def __init__(self, type: Type, identifier: str): + self.type = type + self.identifier = identifier + + def __str__(self): + type_args_str = ( + f"[{', '.join(str(arg) for arg in self.type.type_args)}]" + if self.type.type_args + else "" + ) + return f"{self.type.basic_type}{type_args_str} {self.identifier}" + + def __repr__(self): + return f"Argument({self.type!r}, {self.identifier!r})" + + def __eq__(self, other): + return self.type == other.type and self.identifier == other.identifier + + def as_field(self) -> Field: + return self.type.as_field(self.identifier) + + +class SyscallDef: + def __init__(self, name: str, arg_list: List[Argument]): + self.name = name + self.arg_list = arg_list + + def __str__(self): + arg_list_str = ", ".join(str(arg) for arg in self.arg_list) + return f"{self.name}({arg_list_str})" + + def __repr__(self): + return f"SyscallDef({self.name!r}, {self.arg_list!r})" + + def __eq__(self, other): + return self.name == other.name and self.arg_list == other.arg_list + + +class Token: + def __init__(self, token_type: str, token_value: str): + self.token_type = token_type + self.token_value = token_value + + def __str__(self): + return f"{self.token_type}({self.token_value})" + + def __repr__(self): + return f"Token({self.token_type!r}, {self.token_value!r})" + + def __eq__(self, other): + return ( + self.token_type == other.token_type + and self.token_value == other.token_value + ) + + +def tokenize(input_str: str) -> List[Token]: + # match words, parentheses, brackets, commas, and equal signs + # also match double-quoted strings + tokens = re.findall(r'\w+|[()[\],=]|"[^"]*"', input_str) + token_list = [] + + for token in tokens: + if token in FIELD_CLASSES: + token_list.append(Token("basic_type", token)) + elif token in ["(", ")", "[", "]", ",", "="]: + token_list.append(Token(token, token)) + elif token.startswith('"') and token.endswith('"'): + token_list.append(Token("string_literal", token[1:-1])) + else: + token_list.append(Token("identifier", token)) + + return token_list + + +def parse_type(tokens: List[Token]) -> Type: + basic_type = parse_basic_type(tokens) + type_args = parse_type_args(tokens) + + return Type(basic_type, type_args) + + +def parse_basic_type(tokens: List[Token]) -> BasicType: + return BasicType(tokens.pop(0).token_value) + + +def parse_type_args(tokens: List[Token]) -> List[TypeArg]: + type_args = [] + + if tokens and tokens[0].token_type == "[": + tokens.pop(0) # Remove '[' + while tokens and tokens[0].token_type != "]": + type_args.append(parse_type_arg(tokens)) + if tokens and tokens[0].token_type == ",": + tokens.pop(0) # Remove ',' + if tokens and tokens[0].token_type == "]": + tokens.pop(0) # Remove ']' + + return type_args + + +def parse_value(tokens: List[Token]) -> Union[Type, str, int]: + if tokens[0].token_type == "basic_type": + return parse_type(tokens) + + if tokens[0].token_type == '"': + accumulator = [] + tokens.pop(0) + while tokens and tokens[0].token_type != '"': + accumulator.append(tokens.pop(0).token_value) + if tokens: + tokens.pop(0) # Remove ending '"' + else: + raise ValueError("Unterminated string") + return ''.join(accumulator) + + value = tokens.pop(0).token_value + try: + return int(value) + except ValueError: + return value + + +def parse_type_arg(tokens: List[Token]) -> TypeArg: + if tokens[0].token_type != "identifier": + remaining = ''.join(token.token_value for token in tokens) + raise ValueError( + f"Expected identifier but found '{tokens[0].token_value}' of type '{tokens[0].token_type}' while parsing '{remaining}'" + ) + if tokens[1].token_type != "=": + remaining = ''.join(token.token_value for token in tokens) + raise ValueError( + f"Expected '=' following '{tokens[0].token_value}' while parsing '{remaining}'" + ) + + attr_identifier = tokens.pop(0).token_value + tokens.pop(0) # Remove '=' + value = parse_value(tokens) + return TypeArg(attr_identifier, value) + + +# Updated parse function +def parse(input_str: str) -> SyscallDef: + tokens = tokenize(input_str) + syscall_name = tokens.pop(0).token_value + tokens.pop(0) # Remove '(' + + arg_list = [] + while tokens and tokens[0].token_type != ")": + type = parse_type(tokens) + arg_name = tokens.pop(0).token_value + arg_list.append(Argument(type, arg_name)) + + if tokens and tokens[0].token_type == ",": + tokens.pop(0) # Remove ',' + + if tokens and tokens[0].token_type == ")": + tokens.pop(0) # Remove ')' + + return SyscallDef(syscall_name, arg_list) + + +def parse_syscall(line: str) -> Tuple[str, List[Field]]: + syscall_def = parse(line) + fields = [arg.as_field() for arg in syscall_def.arg_list] + return syscall_def.name, fields diff --git a/tools/generate_syscall/downstream_syscall_parser_test.py b/tools/generate_syscall/downstream_syscall_parser_test.py new file mode 100644 index 0000000..e16fbea --- /dev/null +++ b/tools/generate_syscall/downstream_syscall_parser_test.py @@ -0,0 +1,271 @@ +# Copyright 2024 Google LLC +# +# 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. + +import unittest +from sockfuzzer.tools.generate_syscall.downstream_syscall_parser import ( + tokenize, + parse, + Type, + Token, + TypeArg, + Argument, + SyscallDef, + parse_basic_type, + parse_value, + parse_type_arg, + parse_type, + BasicType, +) +from sockfuzzer.tools.generate_syscall.downstream_field import ( + Declared, + FIELD_CLASSES, + Ptr, + UInt64, + Enum +) + + +class TestParser(unittest.TestCase): + def test_tokenize(self): + input_str = "syscall_name(Ptr[ref=Declared[Int32]] arg1, UInt64 arg2)" + expected_tokens = [ + Token("identifier", "syscall_name"), + Token("(", "("), + Token("basic_type", "Ptr"), + Token("[", "["), + Token("identifier", "ref"), + Token("=", "="), + Token("basic_type", "Declared"), + Token("[", "["), + Token("basic_type", "Int32"), + Token("]", "]"), + Token("]", "]"), + Token("identifier", "arg1"), + Token(",", ","), + Token("basic_type", "UInt64"), + Token("identifier", "arg2"), + Token(")", ")"), + ] + self.assertEqual(tokenize(input_str), expected_tokens) + + def test_parse_basic_type(self): + tokens = [Token("basic_type", "Ptr")] + basic_type = parse_basic_type(tokens) + self.assertEqual(basic_type.name, "Ptr") + + def test_parse_type_arg(self): + tokens = [ + Token("identifier", "ref"), + Token("=", "="), + Token("basic_type", "Declared"), + Token("[", "["), + Token("identifier", "c_type"), + Token("=", "="), + Token("basic_type", "Int32"), + Token("]", "]"), + ] + type_arg = parse_type_arg(tokens) + self.assertEqual(type_arg.attr_identifier, "ref") + ref_value = type_arg.value + self.assertIsInstance(ref_value, Type) + assert isinstance(ref_value, Type) + + def test_parse_type_arg_string(self): + input = 'c_type="struct msghdr"' + tokens = tokenize(input) + type_arg = parse_type_arg(tokens) + self.assertEqual(type_arg.attr_identifier, "c_type") + self.assertEqual(type_arg.value, "struct msghdr") + + def test_parse_type_missing_ref(self): + # Ptr[Declared[Int32]] + tokens = [ + Token("basic_type", "Ptr"), + Token("[", "["), + Token("basic_type", "Declared"), + Token("[", "["), + Token("basic_type", "Int32"), + Token("]", "]"), + Token("]", "]"), + ] + try: + type_arg = parse_type_arg(tokens) + except ValueError as e: + self.assertEqual( + str(e), + "Expected identifier but found 'Ptr' of type 'basic_type' while parsing 'Ptr[Declared[Int32]]'" + ) + else: + self.fail("Expected ValueError") + + def test_basic_type(self): + tokens = [Token("basic_type", "UInt32")] + result = parse_value(tokens) + self.assertEqual(str(result), "UInt32") + + def test_quoted_string(self): + tokens = [ + Token('"', '"'), + Token("identifier", "hello"), + Token("identifier", "world"), + Token('"', '"'), + ] + result = parse_value(tokens) + self.assertEqual(result, "helloworld") + + def test_unquoted_string(self): + tokens = [Token("identifier", "unquoted_string")] + result = parse_value(tokens) + self.assertEqual(result, "unquoted_string") + + def test_integer(self): + tokens = [Token("identifier", "42")] + result = parse_value(tokens) + self.assertEqual(result, 42) + + def test_unterminated_string(self): + tokens = [Token('"', '"'), + Token("identifier", "unterminated"), + Token("identifier", "string"), + ] + with self.assertRaises(ValueError): + parse_value(tokens) + + def test_uint64_cast(self): + # UInt64[cast=caddr_t] + tokens = [ + Token("basic_type", "UInt64"), + Token("[", "["), + Token("identifier", "cast"), + Token("=", "="), + Token("identifier", "caddr_t"), + Token("]", "]"), + ] + type = parse_type(tokens) + field = type.as_field("field_name") + self.assertEqual(field.name, "field_name") + assert isinstance(field, UInt64) + self.assertEqual(field.c_type, "unsigned long") + self.assertEqual(field.cast, "caddr_t") + + def test_enum_invalid(self): + """ + Using c_type is not valid for Enum. Make sure we raise a ValueError. + """ + # Enum[c_type=MadviseFlags] + tokens = [ + Token("basic_type", "Enum"), + Token("[", "["), + Token("identifier", "c_type"), + Token("=", "="), + Token("identifier", "MadviseFlags"), + Token("]", "]"), + ] + type = parse_type(tokens) + self.assertRaises(ValueError, type.as_field) + + def test_enum(self): + # Enum[proto_type=MadviseFlags] + tokens = [ + Token("basic_type", "Enum"), + Token("[", "["), + Token("identifier", "proto_type"), + Token("=", "="), + Token("identifier", "MadviseFlags"), + Token("]", "]"), + ] + type = parse_type(tokens) + field = type.as_field("madvise") + self.assertEqual(field.name, "madvise") + assert isinstance(field, Enum) + self.assertEqual(field.c_type, "int") + self.assertIsNone(field.cast) + self.assertEqual(field.proto_type, "MadviseFlags") + + def test_parse(self): + input_str = "syscall_name(Ptr[ref=Declared[c_type=Int32]] arg1, UInt64 arg2)" + expected_syscall_def = SyscallDef( + "syscall_name", + [ + Argument( + Type( + BasicType("Ptr"), + [ + TypeArg( + "ref", + Type( + BasicType("Declared"), + [TypeArg("c_type", Type(BasicType("Int32"), []))], + ), + ), + ], + ), + "arg1", + ), + Argument(Type(BasicType("UInt64"), []), "arg2"), + ], + ) + self.assertEqual(parse(input_str), expected_syscall_def) + + def test_as_field_basic(self): + arg = Argument(Type(BasicType("UInt64"), []), "arg2") + field = arg.as_field() + + # Check field properties + self.assertEqual(field.name, "arg2") + self.assertIs(type(field), FIELD_CLASSES["UInt64"]) + self.assertEqual(field.proto_type, "uint64") + self.assertEqual(field.c_type, "unsigned long") + self.assertIsNone(field.max_value) + + def test_as_field_nested(self): + # Ptr[field=Declared[c_type=int]] + arg = Argument( + Type( + BasicType("Ptr"), + [ + TypeArg( + "field", Type(BasicType("Declared"), [TypeArg("c_type", "int")]) + ) + ], + ), + "arg1", + ) + field = arg.as_field() + + # Check field properties + self.assertEqual(field.name, "arg1") + self.assertIs(type(field), Ptr) + self.assertIsNotNone(field.field) + + # Check nested field properties + nested_field = field.field + self.assertIsInstance(nested_field, Declared) + assert isinstance(nested_field, Declared) + print(nested_field) + self.assertEqual(nested_field.c_type, "int") + + def test_retval_name(self): + # Retval retval + # arg = Argument(Type(BasicType("Retval"), []), "retval") + tokens = [ + Token("basic_type", "Retval"), + Token("identifier", "retval"), + ] + + parse_type_arg + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/generate_syscall/downstream_syscall_test.py b/tools/generate_syscall/downstream_syscall_test.py new file mode 100644 index 0000000..b2e6725 --- /dev/null +++ b/tools/generate_syscall/downstream_syscall_test.py @@ -0,0 +1,139 @@ +# Copyright 2024 Google LLC +# +# 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. + +from sockfuzzer.tools.generate_syscall.downstream_syscall import DownstreamSyscall +from sockfuzzer.tools.generate_syscall.downstream_field import Nullptr, Literal, EmptyBuffer, Flags, EmptyArray +import unittest + + +class ParserTest(unittest.TestCase): + def test_parses_simple(self): + syscall = DownstreamSyscall.from_line("syscall(Nullptr retval)") + self.assertEqual(syscall.name, "syscall") + self.assertEqual(len(syscall.fields), 1) + self.assertEqual(syscall.fields[0].name, "retval") + self.assertIsInstance(syscall.fields[0], Nullptr) + + def test_parses_arg(self): + syscall = DownstreamSyscall.from_line("bla(Literal[value=1] val)") + # Use plain assert to inform type system + assert isinstance(syscall.fields[0], Literal) + self.assertEqual(syscall.fields[0].value, 1) + + def test_parses_kwarg(self): + syscall = DownstreamSyscall.from_line("bla(Literal[value=1] val)") + assert isinstance(syscall.fields[0], Literal) + self.assertEqual(syscall.fields[0].value, 1) + + def test_parses_kwargs(self): + syscall = DownstreamSyscall.from_line( + "bla(EmptyArray[c_type=\"unsigned long\", fixed_size=2] val)" + ) + assert isinstance(syscall.fields[0], EmptyArray) + self.assertEqual(syscall.fields[0].c_type, "unsigned long") + self.assertEqual(syscall.fields[0].fixed_size, 2) + + def test_parses_args_and_kwargs(self): + syscall = DownstreamSyscall.from_line( + "bla(Flags[proto_type=EnumName, c_type=\"unsigned long\"] val)" + ) + assert isinstance(syscall.fields[0], Flags) + self.assertEqual(syscall.fields[0].c_type, "unsigned long") + self.assertEqual(syscall.fields[0].proto_type, "EnumName") + + def test_parses_cast(self): + syscall = DownstreamSyscall.from_line("dummy(Literal[cast=my_cast_t, value=0] val)") + self.assertEqual(len(syscall.fields), 1) + assert syscall.fields[0].cast is not None + self.assertEqual(syscall.fields[0].cast.strip(), "my_cast_t") + + def test_buffer_with_size(self): + syscall = DownstreamSyscall.from_line( + "read(EmptyBuffer[size_given_by=size] buf, UInt32 size)" + ) + self.assertEqual(len(syscall.fields), 2) + assert isinstance(syscall.fields[0], EmptyBuffer) + self.assertEqual(syscall.fields[0].size_given_by, "size") + self.assertEqual(syscall.fields[1].name, "size") + self.assertEqual(syscall.fields[1], syscall.field_by_name("size")) + + +SIMPLE_SYSCALL_DEF = "simple(UInt64 bla)" +SIMPLE_CALLER_CALL = "simple_wrapper(bla);" +SIMPLE_CALLING_CODE = """\ +case BsdSyscall::kSimple: { + auto args = syscall.simple(); + unsigned long bla = args.bla(); + simple_wrapper(bla); + break; +} +""" +SIMPLE_PROTO = """\ +message SimpleMessage { + required uint64 bla = 1; +} +""" + +MINCORE_SYSCALL_DEF = "mincore(UInt64 addr, CappedSize len, EmptyBuffer[size_given_by=len] vec, Retval retval)" +MINCORE_CALLER_CALL = "mincore_wrapper(addr, len, vec.data(), &(retval));" +MINCORE_CALLING_CODE = """\ +case BsdSyscall::kMincore: { + auto args = syscall.mincore(); + int retval; + unsigned long len = (args.len() % 4096); + unsigned long addr = args.addr(); + std::vector vec(len); + mincore_wrapper(addr, len, vec.data(), &(retval)); + break; +} +""" +MINCORE_PROTO = """\ +message MincoreMessage { + required uint64 addr = 1; + required uint64 len = 2; +} +""" + + +class CodegenTest(unittest.TestCase): + # These tests are simple, just making sure *something* is generated. + # The real test of codegen and syscalls.defs is whether the actual + # generated code compiles. + def test_codegen_call_simple(self): + syscall = DownstreamSyscall.from_line(SIMPLE_SYSCALL_DEF) + self.assertEqual(syscall.caller_call.strip(), SIMPLE_CALLER_CALL) + + def test_codegen_caller_case_simple(self): + syscall = DownstreamSyscall.from_line(SIMPLE_SYSCALL_DEF) + self.assertEqual(syscall.calling_code, SIMPLE_CALLING_CODE) + + def test_codegen_proto_simple(self): + syscall = DownstreamSyscall.from_line(SIMPLE_SYSCALL_DEF) + self.assertEqual(syscall.proto_message, SIMPLE_PROTO) + + def test_codegen_call_mincore(self): + syscall = DownstreamSyscall.from_line(MINCORE_SYSCALL_DEF) + self.assertEqual(syscall.caller_call.strip(), MINCORE_CALLER_CALL) + + def test_codegen_caller_case_mincore(self): + syscall = DownstreamSyscall.from_line(MINCORE_SYSCALL_DEF) + self.assertEqual(syscall.calling_code, MINCORE_CALLING_CODE) + + def test_codegen_proto_mincore(self): + syscall = DownstreamSyscall.from_line(MINCORE_SYSCALL_DEF) + self.assertEqual(syscall.proto_message, MINCORE_PROTO) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/generate_syscall/generate_defs_from_upstream.sh b/tools/generate_syscall/generate_defs_from_upstream.sh new file mode 100755 index 0000000..25b209c --- /dev/null +++ b/tools/generate_syscall/generate_defs_from_upstream.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +# Copyright 2024 Google LLC +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. The rights granted to you under the License +# may not be used to create, or enable the creation or redistribution of, +# unlawful or unlicensed copies of an Apple operating system, or to +# circumvent, violate, or enable the circumvention or violation of, any +# terms of an Apple operating system software license agreement. +# +# Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + + +# Parse syscalls.master into defs +python3 tools/generate_syscall/upstream_syscall_manager.py \ + -i third_party/xnu/bsd/kern/syscalls.master \ + --defs tools/generate_syscall/syscalls.defs \ + -x nosys \ + -x enosys \ + -D SOCKETS \ + -D NFSSERVER \ + -D NFSCLIENT \ + -D SYSV_SEM \ + -D SYSV_MSG \ + -D SYSV_SHM \ + -D CONFIG_EXT_RESOLVER \ + -D PSYNCH \ + -D SENDFILE \ + -D CONFIG_WORKQUEUE \ + -D CONFIG_MACF \ + -D CONFIG_MEMORYSTATUS \ + -D CONFIG_PROC_UUID_POLICY \ + -D CONFIG_COALITIONS \ + -D CONFIG_CSR \ + -D CONFIG_CODE_DECRYPTION \ + -D CONFIG_TELEMETRY \ + -D PGO \ + -D CONFIG_PERSONAS \ + -D NECP \ + -D OLD_SEMWAIT_SIGNAL \ + # -D NETWORKING \ + # -D SKYWALK \ + # -D CONFIG_VFORK \ + # -D CONFIG_EMBEDDED \ + +# Defines should match LIBKERN_DEFINES + +# syscalls.defs should be checked into version control. +# User should manually change the types to work with +# code generation. diff --git a/tools/generate_syscall/templates/wrappers_header_preamble.h b/tools/generate_syscall/templates/wrappers_header_preamble.h new file mode 100644 index 0000000..4fe4b34 --- /dev/null +++ b/tools/generate_syscall/templates/wrappers_header_preamble.h @@ -0,0 +1,150 @@ +/* + * Copyright 2024 Google LLC + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifdef LIBXNU_BUILD + +// clang-format off +#include +#include + +#include +#include +// clang-format on + +#include "bsd/net/necp.h" +#include "bsd/netinet/in.h" +#include "bsd/netinet/in_tclass.h" +#include "bsd/sys/_types/_sigset_t.h" +#include "bsd/sys/guarded.h" +#include "bsd/sys/kernel_types.h" +#include "bsd/sys/msg.h" +#include "bsd/sys/persona.h" +#include "bsd/sys/poll.h" +#include "bsd/sys/select.h" +#include "bsd/sys/shm.h" +#include "bsd/sys/signal.h" +#include "bsd/sys/socket.h" +#include "bsd/sys/spawn_internal.h" +#include "bsd/sys/timex.h" +#include "bsd/sys/types.h" +#else +// TODO(nedwill): is it valid to have this section? +#include +#include + +typedef __uint64_t user64_addr_t; +typedef struct user64_sa_endpoints sa_endpoints_t; +typedef unsigned char uuid_t[16]; +typedef u_int64_t user_addr_t; +typedef u_int32_t socklen_t; +typedef __uint32_t sae_associd_t; +typedef __uint32_t sae_connid_t; +typedef int64_t user_ssize_t; +typedef char *caddr_t; +struct user64_sa_endpoints { + unsigned int sae_srcif; /* optional source interface */ + user64_addr_t sae_srcaddr; /* optional source address */ + socklen_t sae_srcaddrlen; /* size of source address */ + user64_addr_t sae_dstaddr; /* destination address */ + socklen_t sae_dstaddrlen; /* size of destination address */ +}; +struct user64_msghdr { + user64_addr_t msg_name; /* optional address */ + socklen_t msg_namelen; /* size of address */ + user64_addr_t msg_iov; /* scatter/gather array */ + int msg_iovlen; /* # elements in msg_iov */ + user64_addr_t msg_control; /* ancillary data, see below */ + socklen_t msg_controllen; /* ancillary data buffer len */ + int msg_flags; /* flags on received message */ +}; +typedef uint32_t mach_port_name_t; +typedef u_int64_t user_size_t; +typedef __uint64_t guardid_t; + +typedef uid_t au_id_t; +typedef pid_t au_asid_t; +#define NFS_MAX_FH_SIZE 128 +struct fhandle { + unsigned int fh_len; /* length of file handle */ + unsigned char fh_data[NFS_MAX_FH_SIZE]; /* file handle value */ +}; +typedef struct fhandle fhandle_t; + +union sigval { + /* Members as suggested by Annex C of POSIX 1003.1b. */ + int sival_int; + void *sival_ptr; +}; + +typedef struct __siginfo { + int si_signo; /* signal number */ + int si_errno; /* errno association */ + int si_code; /* signal code */ + pid_t si_pid; /* sending process */ + uid_t si_uid; /* sender's ruid */ + int si_status; /* exit value */ + void *si_addr; /* faulting instruction */ + union sigval si_value; /* signal value */ + long si_band; /* band event for SIGPOLL */ + unsigned long __pad[7]; /* Reserved for Future Use */ +} siginfo_t; + +#define GRAFTDMG_SECURE_BOOT_CRYPTEX_ARGS_VERSION 1 +#define MAX_GRAFT_ARGS_SIZE 512 + +/* Flag values for secure_boot_cryptex_args.sbc_flags */ +#define SBC_PRESERVE_MOUNT 0x0001 /* Preserve underlying mount until shutdown */ +#define SBC_ALTERNATE_SHARED_REGION 0x0002 /* Binaries within should use alternate shared region */ +#define SBC_SYSTEM_CONTENT 0x0004 /* Cryptex contains system content */ +#define SBC_PANIC_ON_AUTHFAIL 0x0008 /* On failure to authenticate, panic */ +#define SBC_STRICT_AUTH 0x0010 /* Strict authentication mode */ +#define SBC_PRESERVE_GRAFT 0x0020 /* Preserve graft itself until unmount */ + +typedef struct secure_boot_cryptex_args { + u_int32_t sbc_version; + u_int32_t sbc_4cc; + int sbc_authentic_manifest_fd; + int sbc_user_manifest_fd; + int sbc_payload_fd; + u_int64_t sbc_flags; +} __attribute__((aligned(4), packed)) secure_boot_cryptex_args_t; + +typedef union graft_args { + u_int8_t max_size[MAX_GRAFT_ARGS_SIZE]; + secure_boot_cryptex_args_t sbc_args; +} graftdmg_args_un; +#endif /* LIBXNU_BUILD */ + +// TODO(nedwill): these names are incompatible with the host so we should generate +// alternative names as manually done here for now. +typedef int fuzz_sem_t; +struct fuzz_timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +typedef enum { FUZZ_P_ALL, FUZZ_P_PID, FUZZ_P_PGID } fuzz_idtype_t; diff --git a/tools/generate_syscall/upstream_syscall.py b/tools/generate_syscall/upstream_syscall.py new file mode 100644 index 0000000..f4fa56d --- /dev/null +++ b/tools/generate_syscall/upstream_syscall.py @@ -0,0 +1,187 @@ +# Copyright 2024 Google LLC +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. The rights granted to you under the License +# may not be used to create, or enable the creation or redistribution of, +# unlawful or unlicensed copies of an Apple operating system, or to +# circumvent, violate, or enable the circumvention or violation of, any +# terms of an Apple operating system software license agreement. +# +# Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + +from dataclasses import dataclass +from typing import List +import re +import textwrap + + +@dataclass +class ParsedArgument: + type_: str + raw_name: str + syscall_name: str + + @property + def name(self): + if self.raw_name in [self.syscall_name, "new", "class"]: + return self.raw_name + "_arg" + return self.raw_name + + def as_decl_argument(self): + type_ = self.type_ + if type_ == "sem_t *": + type_ = "fuzz_sem_t *" + if type_ == "idtype_t": + type_ = "fuzz_idtype_t" + if type_ == "semun_t": + type_ = "user_addr_t" + if type_ == "const struct sigset_t *": + type_ = "u_int32_t *" + if type_ == "struct timezone *": + type_ = "struct fuzz_timezone *" + if type_.endswith("*"): + arg = type_ + self.name + else: + arg = type_ + " " + self.name + return arg + + def as_struct_field_assignment(self): + cast = "" + suffix = "" + if self.type_ in ( + "caddr_t", + "idtype_t", + "semun_t", + "uuid_t", + ) or self.type_.endswith("*"): + cast = "(user_addr_t) " + return f".{self.raw_name} = {cast}{self.name}{suffix}," + + +@dataclass +class CompilerFlags: + # These probably don't need to be lists + defined: List[str] + undefined: List[str] + + +@dataclass +class UpstreamSyscall: + number: int + name: str + args: List[ParsedArgument] + return_type: str + call: str + comment: str + compiler_flags: CompilerFlags + + @classmethod + def from_line(cls, line, defined_flag, undefined_flag): + # We are thus parsing a syscall. Example: + # 33 AUE_ACCESS ALL { int access(user_addr_t path, int flags); } + # It has approximately the following form: + # [number] [?] [?] [raw] [comment] + # where [raw] = { [return_type] [name]([args...]); } + line = line.replace("\t", " ") + number = int(line.split()[0]) + call = line[line.index("{") : line.index("}") + 1] + match = re.match(r"\{\s*(\w+)\s*(\w+)\s*\((.*)\).*", call) + if match is None: + raise ValueError(f"Failed to parse {call}") + return_type, name, args_raw = match.groups() + args = [] + if args_raw != "void": + for arg in args_raw.split(","): + arg = arg.strip() + arg_name_start = max(arg.rfind(" "), arg.rfind("*")) + 1 + assert arg_name_start > 0 + arg_type, arg_name = arg[:arg_name_start], arg[arg_name_start:] + args.append(ParsedArgument(arg_type.strip(), arg_name.strip(), name)) + comment = line[line.index("}") + 1 :].strip() + compiler_flags = CompilerFlags( + [defined_flag] if defined_flag is not None else [], + [undefined_flag] if undefined_flag is not None else [], + ) + return cls(number, name, args, return_type, call, comment, compiler_flags) + + @property + def wrapper_name(self): + return self.name + "_wrapper" + + @property + def all_args(self): + if self.name == "exit": + ret_arg = ParsedArgument("int *", "retval", self.name) + else: + ret_arg = ParsedArgument(self.return_type + " *", "retval", self.name) + return self.args + [ret_arg] + + @property + def wrapper_decl(self): + args = ", ".join(arg.as_decl_argument() for arg in self.all_args) + return f"int {self.wrapper_name}({args})" + + @property + def wrapper_struct_name(self): + if self.name.startswith("sys_"): + return self.name[4:] + "_args" + return self.name + "_args" + + @property + def wrapper_struct(self): + struct = "struct " + self.wrapper_struct_name + " args = {\n" + for arg in self.args: + if arg.name == "retval": + continue + struct += " " + arg.as_struct_field_assignment() + "\n" + struct += "};\n" + return struct + + @property + def wrapper_real_call(self): + retval_cast = "" + if self.name.startswith("psynch") and self.name not in ( + "psynch_rw_downgrade", + "psynch_cvclrprepost", + ): + retval_cast = "(unsigned int *) " + if self.name == "audit_session_self": + retval_cast = "(unsigned int *) " + return self.name + "(current_proc(), &args, " + retval_cast + "retval)" + + @property + def wrapper_call_and_return(self): + if self.return_type == "void": + return f"{self.wrapper_real_call};\nreturn 0;\n" + return f"return {self.wrapper_real_call};\n" + + @property + def wrapper_body(self): + return self.wrapper_struct + self.wrapper_call_and_return + + @property + def wrapper_impl(self): + wrapper = self.wrapper_decl + " {\n" + wrapper += textwrap.indent(self.wrapper_body, " ") + wrapper += "}\n" + return wrapper + + @property + def def_entry(self): + args = ", ".join(arg.as_decl_argument() for arg in self.all_args) + return f"{self.name}({args})" diff --git a/tools/generate_syscall/upstream_syscall_manager.py b/tools/generate_syscall/upstream_syscall_manager.py new file mode 100644 index 0000000..420f496 --- /dev/null +++ b/tools/generate_syscall/upstream_syscall_manager.py @@ -0,0 +1,263 @@ +# Copyright 2024 Google LLC +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. The rights granted to you under the License +# may not be used to create, or enable the creation or redistribution of, +# unlawful or unlicensed copies of an Apple operating system, or to +# circumvent, violate, or enable the circumvention or violation of, any +# terms of an Apple operating system software license agreement. +# +# Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + +""" +Parses syscalls.master in UpstreamSyscalls and writes syscall_wrappers.h/cc + +Filters the UpstreamSyscalls based on compiler flags and explicitly ignored names +""" +from tools.generate_syscall.upstream_syscall import UpstreamSyscall +from typing import List, IO +import os +import sys +import textwrap + +debug_enabled = os.environ.get("GENERATE_SYSCALL_DEBUG", "0") == "1" + + +def parse_syscalls(file: IO[str]) -> List[UpstreamSyscall]: + """ + Read file and return a list of UpstreamSyscalls. + + file should be in the format of xnu/bsd/kern/syscall.master. + """ + defined_flag = None # flag within an if block + undefined_flag = None # previous if flag when in an else block + + parsed_syscalls = [] + + for line in file.readlines(): + line = line.strip() + if len(line) == 0: + # empty + continue + if line.startswith(";"): + # comment + continue + if line.startswith("#include"): + # no thanks + continue + if line.startswith("#if"): + # entering if block + defined_flag = line.split()[1] + continue + if line.startswith("#else"): + # entering an else block + assert defined_flag is not None + undefined_flag = defined_flag + defined_flag = None + continue + if line.startswith("#endif"): + # exiting else block + assert defined_flag is None # does every if have an else? + undefined_flag = None + continue + + parsed = UpstreamSyscall.from_line(line, defined_flag, undefined_flag) + + parsed_syscalls.append(parsed) + return parsed_syscalls + + +def filter_syscalls( + syscalls: List[UpstreamSyscall], defined_flags: List[str], delete_names: List[str] +) -> List[UpstreamSyscall]: + filtered = [] + for syscall in syscalls: + if syscall.name in delete_names: + continue + required_flags = syscall.compiler_flags.defined + if any(required not in defined_flags for required in required_flags): + continue + banned_flags = syscall.compiler_flags.undefined + if any(banned in defined_flags for banned in banned_flags): + continue + filtered.append(syscall) + return filtered + + +class UpstreamSyscallManager: + """ + Syscalls sourced from syscalls.master + """ + + def __init__(self, syscalls: List[UpstreamSyscall]): + self.syscalls = {} + for syscall in syscalls: + self.syscalls[syscall.name] = syscall + + @classmethod + def from_path(cls, path, flags, delete_names): + with open(path, "r") as syscall_master_file: + parsed_syscalls = parse_syscalls(syscall_master_file) + # Only print these if enabled by the user via the environment + if debug_enabled: + print(f"Parsed {len(parsed_syscalls)} syscalls from {path}") + print(f"Filtering with flags: {flags}") + print(f"Deleting syscalls: {delete_names}") + + syscalls = filter_syscalls(parsed_syscalls, flags, delete_names) + if debug_enabled: + print(f"After filtering: {len(syscalls)} syscalls remain") + return cls(syscalls) + + def find(self, name): + if name not in self.syscalls: + if debug_enabled: + print(f"Syscall '{name}' not found.") + print(f"Total available syscalls: {len(self.syscalls)}") + print("First 10 available syscalls:") + for key in list(self.syscalls.keys())[:10]: + print(f" {key}") + print("...") + return None + return self.syscalls[name] + + def write_wrappers_header(self, out, header_preamble): + preamble = """\ + // Autogenerated by generate_wrappers.py + + #ifndef SYSCALL_WRAPPERS_H_ + #define SYSCALL_WRAPPERS_H_ + + #ifdef __cplusplus + extern "C" { + #endif + """ + preamble += header_preamble.read() + output = textwrap.dedent(preamble) + + for syscall in self.syscalls.values(): + output += syscall.wrapper_decl + ";\n" + + output += """ + #ifdef __cplusplus + } + #endif + """ + + output += "\n#endif // SYSCALL_WRAPPERS_H_\n" + out.write(output) + + def write_wrappers(self, out): + preamble = """\ + // Autogenerated by generate_wrappers.py + + #include "syscall_wrappers_generated.h" + + """ + output = textwrap.dedent(preamble) + + for syscall in self.syscalls.values(): + output += syscall.wrapper_impl + out.write(output) + + def write_defs(self, out): + preamble = """\ + # Autogenerated by generate_wrappers.py + + # Manually override types to work with generate_callers + + """ + output = textwrap.dedent(preamble) + + for syscall in self.syscalls.values(): + output += syscall.def_entry + "\n" + out.write(output) + + +if __name__ == "__main__": + + import argparse + + parser = argparse.ArgumentParser( + description="Generate wrappers based on syscall.master" + ) + parser.add_argument( + "-i", + dest="input", + required=True, + metavar="FILE", + help="Path to syscalls.master.", + ) + parser.add_argument( + "-D", + dest="flags", + action="append", + help="Define a compiler flag for filtering syscalls.", + ) + parser.add_argument( + "-x", + dest="delete_names", + action="append", + help="Remove syscalls with the provided name.", + ) + parser.add_argument( + "--defs", + dest="defs", + metavar="FILE", + help="Path to save syscall defintions for manual typing.", + ) + parser.add_argument( + "--wrappers", + dest="wrappers", + metavar="FILE", + help="Path to save wrappers", + ) + parser.add_argument( + "--wrappers_header", + dest="wrappers_header", + metavar="FILE", + help="Path to save wrappers header", + ) + parser.add_argument( + "--wrappers_header_preamble", + dest="wrappers_header_preamble", + metavar="FILE", + help="Path to file containing preamble for wrappers_header", + ) + args = parser.parse_args() + + upstream_syscalls = UpstreamSyscallManager.from_path( + args.input, args.flags, args.delete_names + ) + if upstream_syscalls is None: + print(f"Failed to parse {args.input}") + sys.exit(1) + + if args.wrappers_header: + assert args.wrappers_header_preamble + with open(args.wrappers_header_preamble) as header_preamble: + with open(args.wrappers_header, "w") as out: + upstream_syscalls.write_wrappers_header(out, header_preamble) + + if args.wrappers: + with open(args.wrappers, "w") as out: + upstream_syscalls.write_wrappers(out) + + if args.defs: + with open(args.defs, "w") as out: + upstream_syscalls.write_defs(out) diff --git a/tools/generate_syscall/upstream_syscall_manager_test.py b/tools/generate_syscall/upstream_syscall_manager_test.py new file mode 100644 index 0000000..7e8f38d --- /dev/null +++ b/tools/generate_syscall/upstream_syscall_manager_test.py @@ -0,0 +1,193 @@ +# Copyright 2024 Google LLC +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. The rights granted to you under the License +# may not be used to create, or enable the creation or redistribution of, +# unlawful or unlicensed copies of an Apple operating system, or to +# circumvent, violate, or enable the circumvention or violation of, any +# terms of an Apple operating system software license agreement. +# +# Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + +from tools.generate_syscall.upstream_syscall_manager import ( + parse_syscalls, + filter_syscalls, +) +import unittest +from io import StringIO + +SYSCALL_WAIT4 = """ +#include dummy + +; comment + +7 AUE_WAIT4 ALL { int wait4(int pid, user_addr_t status, int options, user_addr_t rusage) NO_SYSCALL_STUB; } + +; command + +""" + +WAIT4_IMPL = """\ +int wait4_wrapper(int pid, user_addr_t status, int options, user_addr_t rusage, int *retval) { + struct wait4_args args = { + .pid = pid, + .status = status, + .options = options, + .rusage = rusage, + }; + return wait4(current_proc(), &args, retval); +} +""" + +SYSCALLS_WITH_DEFS = """ +#if COMPILE_FOO +20 AUE_NULL ALL { int foo(int rval) NO_SYSCALL_STUB; } +#else +20 AUE_NULL ALL { int bar(int rval) NO_SYSCALL_STUB; } +#endif +""" + +SYSCALLS = """ +30 AUE_NULL ALL { int foo(int rval) NO_SYSCALL_STUB; } +31 AUE_NULL ALL { int bar(user_addr_t fd, int rval); } +""" + +SYSCALL_COLLISION = """ +40 AUE_NULL ALL { int collision(void *collision) NO_SYSCALL_STUB; } +""" + +COLLISION_IMPL = """\ +int collision_wrapper(void *collision_arg, int *retval) { + struct collision_args args = { + .collision = (user_addr_t) collision_arg, + }; + return collision(current_proc(), &args, retval); +} +""" + +SYSCALL_PTR_TYPES = """ +50 AUE_NULL ALL { weird_return_t weird(void *ptr, weird_type **val) NO_SYSCALL_STUB; } +""" + +SYSCALL_VOID = """ +60 AUE_NULL ALL { void boring(void); } +""" + + +def parse(string): + return parse_syscalls(StringIO(string)) + + +class ParserTest(unittest.TestCase): + def test_parses_wait4_correctly(self): + parsed = parse(SYSCALL_WAIT4) + self.assertEqual(len(parsed), 1) + wait4 = parsed[0] + self.assertEqual(wait4.number, 7) + self.assertEqual(wait4.name, "wait4") + self.assertEqual(wait4.return_type, "int") + self.assertEqual(len(wait4.args), 4) + + self.assertEqual(wait4.args[0].type_, "int") + self.assertEqual(wait4.args[0].name, "pid") + + self.assertEqual(wait4.args[1].type_, "user_addr_t") + self.assertEqual(wait4.args[1].name, "status") + + self.assertEqual(wait4.args[2].type_, "int") + self.assertEqual(wait4.args[2].name, "options") + + self.assertEqual(wait4.args[3].type_, "user_addr_t") + self.assertEqual(wait4.args[3].name, "rusage") + + def test_avoids_name_collision(self): + parsed = parse(SYSCALL_COLLISION)[0] + + self.assertNotEqual(parsed.name, parsed.args[0].name) + + def test_ptr_types(self): + parsed = parse(SYSCALL_PTR_TYPES) + args = parsed[0].args + + self.assertEqual(args[0].type_, "void *") + self.assertEqual(args[0].name, "ptr") + + self.assertEqual(args[1].type_, "weird_type **") + self.assertEqual(args[1].name, "val") + + def test_void_args(self): + parsed = parse(SYSCALL_VOID) + + self.assertEqual(len(parsed[0].args), 0) + + +class FilterTest(unittest.TestCase): + def test_filter_by_defs(self): + parsed = parse(SYSCALLS_WITH_DEFS) + self.assertEqual(len(parsed), 2) + + filtered = filter_syscalls(parsed, ["COMPILE_FOO"], []) + self.assertEqual(len(filtered), 1) + self.assertEqual(filtered[0].name, "foo") + + filtered = filter_syscalls(parsed, [], []) + self.assertEqual(len(filtered), 1) + self.assertEqual(filtered[0].name, "bar") + + def test_filter_delete(self): + parsed = parse(SYSCALLS) + self.assertEqual(len(parsed), 2) + + filtered = filter_syscalls(parsed, [], ["foo"]) + self.assertEqual(len(filtered), 1) + self.assertEqual(filtered[0].name, "bar") + + +class CodegenTest(unittest.TestCase): + def test_adds_retval(self): + parsed = parse(SYSCALL_WAIT4)[0] + self.assertTrue("int *retval" in parsed.wrapper_decl) + + def test_retval_respects_type(self): + parsed = parse(SYSCALL_PTR_TYPES)[0] + self.assertTrue("weird_return_t *retval" in parsed.wrapper_decl) + + def test_wrapper_decl(self): + parsed = parse(SYSCALL_COLLISION)[0] + self.assertEqual( + parsed.wrapper_decl, + "int collision_wrapper(void *collision_arg, int *retval)", + ) + + def test_wrapper_impl(self): + parsed = parse(SYSCALL_COLLISION)[0] + self.assertEqual(parsed.wrapper_impl, COLLISION_IMPL) + + def test_wait4_impl(self): + parsed = parse(SYSCALL_WAIT4)[0] + self.assertEqual(parsed.wrapper_impl, WAIT4_IMPL) + + def test_def_entry(self): + parsed = parse(SYSCALL_COLLISION)[0] + self.assertEqual( + parsed.def_entry, "collision(void *collision_arg, int *retval)" + ) + + +if __name__ == "__main__": + unittest.main()