Skip to content

Take up openkal 0.11's unit: a handle the caller holds, not a flag (#17) #23

Take up openkal 0.11's unit: a handle the caller holds, not a flag (#17)

Take up openkal 0.11's unit: a handle the caller holds, not a flag (#17) #23

Workflow file for this run

name: numbers
# The kernel's own table, read from the system rather than remembered.
#
# src/sys.h states a number for each call this implementation makes. A number
# that is wrong for one architecture is not a compile error and not a failed
# call: the kernel raises a signal whose name is "bad system call" and the
# program stops, which is a diagnostic that names the program rather than the
# number. So the numbers are read from the system's own header, on both
# architectures, and this workflow is where the reading is recorded.
on:
workflow_dispatch:
push:
paths:
- '.github/workflows/numbers.yml'
jobs:
numbers:
name: the kernel's numbers (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [macos-14, macos-15-intel]
steps:
- uses: actions/checkout@v4
- name: Where the table is
run: |
uname -m
xcrun --show-sdk-path
find "$(xcrun --show-sdk-path)/usr/include/sys" -name 'syscall.h'
- name: Every number this implementation uses
run: |
h="$(xcrun --show-sdk-path)/usr/include/sys/syscall.h"
for n in exit read write close wait4 chdir fchdir fork getpid getuid \
geteuid kill dup getegid getgid ioctl execve umask munmap \
mprotect madvise dup2 fsync gettimeofday readv writev \
ftruncate truncate mmap lseek __getcwd getcwd \
stat stat64 fstat fstat64 lstat lstat64 \
fstatat fstatat64 getdirentries getdirentries64 \
getentropy \
bsdthread_terminate thread_selfid \
open openat openat_nocancel renameat faccessat unlinkat \
readlinkat mkdirat rmdir unlink mkdir rename access \
ulock_wait ulock_wake __ulock_wait __ulock_wake \
futimens utimensat futimes utimes settimeofday \
fchmod fchmodat fcntl \
pipe sigaction \
socket connect accept accept_nocancel bind listen shutdown \
getsockname getpeername setsockopt getsockopt \
sendto recvfrom sendmsg recvmsg socketpair \
poll select pselect kevent; do
printf '%-24s' "$n"
grep -E "^#define[[:space:]]+SYS_${n}[[:space:]]" "$h" | head -1 || echo '(absent)'
done
- name: The whole table, for the record
run: |
h="$(xcrun --show-sdk-path)/usr/include/sys/syscall.h"
wc -l "$h"
grep -c '^#define' "$h"
# Uploaded rather than printed in full: it is nine hundred lines.
cp "$h" "syscall-$(uname -m).h"
- uses: actions/upload-artifact@v4
with:
name: syscall-numbers-${{ matrix.os }}
path: syscall-*.h
# Which numbers this kernel actually serves.
#
# A number that is in the system's table and not in the kernel's is not a
# failed call: the kernel raises a signal whose name is "bad system call" and
# the program stops, which is a diagnostic that names the program rather than
# the number. Reading the table above does not distinguish the two, so this
# job issues each call with arguments that cannot do harm and reports which
# of them the kernel refuses to recognise.
served:
name: the numbers this kernel serves (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [macos-14, macos-15-intel]
steps:
- uses: actions/checkout@v4
- name: Issue each call and report what the kernel does with it
run: |
cat > served.c <<'EOF'
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
static sigjmp_buf back;
static void on_bad(int s) { (void)s; siglongjmp(back, 1); }
extern long syscall(long, ...);
/* Every number src/sys.h states, with arguments chosen so that the
* call either fails harmlessly or does nothing. A call that returns
* or fails is served; a call that does neither raised the signal. */
struct probe { const char* name; long n; long a, b, c, d; };
static const struct probe probes[] = {
{ "close", 6, -1, 0, 0, 0 },
{ "wait4", 7, -1, 0, 1, 0 }, /* WNOHANG */
{ "fchdir", 13, -1, 0, 0, 0 },
{ "dup", 41, -1, 0, 0, 0 },
{ "ioctl", 54, -1, 0, 0, 0 },
{ "munmap", 73, 0, 0, 0, 0 },
{ "mprotect", 74, 0, 0, 0, 0 },
{ "madvise", 75, 0, 0, 0, 0 },
{ "dup2", 90, -1, -1, 0, 0 },
{ "fcntl", 92, -1, 50, 0, 0 }, /* F_GETPATH */
{ "fsync", 95, -1, 0, 0, 0 },
{ "gettimeofday", 116, 0, 0, 0, 0 },
{ "readv", 120, -1, 0, 0, 0 },
{ "writev", 121, -1, 0, 0, 0 },
{ "utimes", 138, 0, 0, 0, 0 },
{ "futimes", 139, -1, 0, 0, 0 },
{ "mmap", 197, 0, 0, 0, 0 },
{ "lseek", 199, -1, 0, 0, 0 },
{ "ftruncate", 201, -1, 0, 0, 0 },
{ "stat64", 338, 0, 0, 0, 0 },
{ "fstat64", 339, -1, 0, 0, 0 },
{ "lstat64", 340, 0, 0, 0, 0 },
{ "getdirentries64",344, -1, 0, 0, 0 },
{ "thread_selfid", 372, 0, 0, 0, 0 },
{ "openat", 463, -2, 0, 0, 0 },
{ "renameat", 465, -2, 0, -2, 0 },
{ "faccessat", 466, -2, 0, 0, 0 },
{ "fstatat64", 470, -2, 0, 0, 0 },
{ "unlinkat", 472, -2, 0, 0, 0 },
{ "readlinkat", 473, -2, 0, 0, 0 },
{ "mkdirat", 475, -2, 0, 0, 0 },
{ "ulock_wait", 515, 0, 0, 0, 0 },
{ "ulock_wake", 516, 0, 0, 0, 0 },
/* The socket calls, with a descriptor that cannot be one. */
{ "recvfrom", 29, -1, 0, 0, 0 },
{ "accept", 30, -1, 0, 0, 0 },
{ "getpeername", 31, -1, 0, 0, 0 },
{ "getsockname", 32, -1, 0, 0, 0 },
{ "select", 93, 0, 0, 0, 0 },
{ "socket", 97, 0, 0, 0, 0 },
{ "connect", 98, -1, 0, 0, 0 },
{ "bind", 104, -1, 0, 0, 0 },
{ "setsockopt", 105, -1, 0, 0, 0 },
{ "listen", 106, -1, 0, 0, 0 },
{ "getsockopt", 118, -1, 0, 0, 0 },
{ "sendto", 133, -1, 0, 0, 0 },
{ "shutdown", 134, -1, 0, 0, 0 },
{ "poll", 230, 0, 0, 0, 0 },
};
int main(void) {
struct sigaction sa; memset(&sa, 0, sizeof sa);
sa.sa_handler = on_bad;
sigaction(SIGSYS, &sa, 0);
int refused = 0;
for (unsigned i = 0; i < sizeof probes / sizeof *probes; i++) {
if (sigsetjmp(back, 1) == 0) {
errno = 0;
long r = syscall(probes[i].n, probes[i].a, probes[i].b,
probes[i].c, probes[i].d);
printf("%-18s %4ld served (returned %ld, errno %d)\n",
probes[i].name, probes[i].n, r, errno);
} else {
printf("%-18s %4ld REFUSED (bad system call)\n",
probes[i].name, probes[i].n);
refused++;
}
}
printf("\n%d refused\n", refused);
return refused ? 1 : 0;
}
EOF
cc -O0 -o served served.c
./served