github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/docs/pseudo_syscalls.md (about)

     1  # Pseudo-syscalls
     2  
     3  Besides regular system calls, a [syscall
     4  description](syscall_descriptions.md) file can also contain
     5  pseudo-syscalls. These are C functions defined in the
     6  executor. When a test program uses a pseudo-syscall, the executor
     7  will generate the pseudo-syscall function code in the resulting C program.
     8  
     9  This allows a test program to have specific code blocks to perform
    10  certain actions, they may also be used as more test-friendly wrappers
    11  for primitive syscalls.
    12  
    13  Use of pseudo-syscalls is generally **discouraged** because they ruin all
    14  advantages of the declarative descriptions (declarativeness, conciseness,
    15  fuzzer control over all aspects, possibility of global improvements to
    16  the logic, static checking, fewer bugs, etc), increase maintenance burden,
    17  are non-reusable and make C reproducers longer. However, syzlang is not
    18  expressive enough to cover all possible cases, so use of pseudo-syscalls
    19  needs to be considered on a case-by-cases basis (additional benefit,
    20  amount of code, possibility of extending syzlang to cover this case, etc).
    21  
    22  ## How to add a pseudo-syscall to the executor
    23  
    24  First, think about the scope of the pseudo-syscall and which systems and
    25  subsystems it will be related to. The executor includes a fixed set of C
    26  header files `executor/common*.h` containing the code of the pseudo-syscalls.
    27  Check if the new one can fit in one of the existing files before creating
    28  a new one.
    29  
    30  For instance, if our new pseudo-syscall is Linux-specific, then
    31  [common_linux.h](../executor/common_linux.h) would be the place to put it.
    32  
    33  The actual pseudo-syscall function may look something like this:
    34  
    35      #if SYZ_EXECUTOR || __NR_syz_mycall
    36      /* Add all the necessary #include and #define headers */
    37  
    38      static long syz_mycall(volatile long a0, volatile long a1)
    39      {
    40              /* Function body */
    41      }
    42      #endif
    43  
    44  Make sure that all the function requirements are met and that it can
    45  be compiled. Note that the function name must start with "syz_". It may
    46  also take a different number of arguments. Type of arguments must be
    47  `volatile long`, return type - `long`. `long` is required to avoid
    48  potential calling convention issues because it is casted to a function
    49  pointer that accepts `long`'s. The reason for `volatile` is interesting:
    50  lots of libc functions are annotated with various argument constraints
    51  (e.g. this argument should not be `NULL`, or that argument must be a
    52  valid file descriptor); C reproducers may call these functions with
    53  constant arguments and compiler may see that some of these constraints
    54  are violated (e.g. passing `NULL` to a `non-NULL` argument, or passing
    55  `-1` as file descriptor) and produce errors/warnings. `volatile` prevents
    56  that.
    57  
    58  Now, to handle the pseudo-syscall properly we have to update the
    59  `linuxSyscallChecks` in
    60  [linux_syscalls.go](../pkg/vminfo/linux_syscalls.go) and add a particular
    61  case for this syscall, enabling it when necessary. If we want to enable
    62  it unconditionally we can simply use `alwaysSupported` for it.
    63  
    64  Finally, run `make generate`. Now you can use it in a syscall
    65  description file as if it was a regular system call:
    66  
    67      syz_mycall(arg0 pid, arg1 const[0])
    68  
    69  <div id="dependencies"/>
    70  
    71  ## External Dependencies
    72  
    73  The implementation must not use any external libraries nor external headers,
    74  except for the most basic and standard ones (like `<unistd.h>` and
    75  `<sys/mman.h>`). In particular, it must not depend on libraries/headers
    76  installed by additional packages nor on headers for recently added kernel
    77  subsystems. External dependencies have proved to be brittle and easily cause
    78  build breakage because all dependencies will be required for any build/run on
    79  the fuzzer and any C reproducer. For example, packages/headers may be missing
    80  on some distros, named differently, be of a wrong version, broken, or conflict
    81  with other headers. Unfortunately, there is no way to reliably specify such
    82  dependencies and requirements for C programs. Therefore, if the pseudo-syscall
    83  requires definitions of some structures, constants, or helper functions, these
    84  should be described in the executor code itself as minimally as possible (they
    85  will be part of C reproducers).
    86  
    87  ## Testing
    88  
    89  Each new pseudo-syscall should have at least one test in `sys/OS/test`.
    90  See [Linux tests](/sys/linux/test) for an example. A tests is just a program
    91  with checked syscall return values. There should be at least one test
    92  that contains "the main successful scenario" of using the pseudo-syscall.
    93  See [io_uring test](/sys/linux/test/io_uring) as a good example.
    94  Such tests are important because they ensure that the pseudo-syscall code
    95  does not contain "stupid" bugs (e.g. crash on NULL-deref each time),
    96  that it is possible for the fuzzer to come up with the successful scenario
    97  (as a combination of the pseudo-syscall and the surrounding descriptions)
    98  and that it will continue to work in future.
    99  See [Testing of descriptions](syscall_descriptions.md#testing)
   100  for details about the tests.