summaryrefslogtreecommitdiffstats
path: root/mkosi.sanitizers/mkosi.postinst
blob: e0ad422f5d6142773a394adb4140c0ee9806ca5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
set -o nounset

LIBSYSTEMD="$(mkosi-chroot ldconfig -p | grep libsystemd.so.0 | sed 's/[^/]*\//\//')"

if [[ ! -f "$BUILDROOT/$LIBSYSTEMD" ]]; then
    exit 0
fi

# Sanitizers log to stderr by default. However, journald's stderr is connected to /dev/null, so we lose
# all the sanitizer logs. To rectify that, let's connect journald's stdout to kmsg so that the sanitizer
# failures end up in the journal.
if [[ -f "$BUILDROOT"/usr/lib/systemd/system/systemd-journald.service ]]; then
    mkdir -p "$BUILDROOT"/etc/systemd/system/systemd-journald.service.d
    cat >"$BUILDROOT"/etc/systemd/system/systemd-journald.service.d/10-stdout-tty.conf <<EOF
[Service]
StandardOutput=kmsg
EOF
fi

# ASAN and syscall filters aren't compatible with each other.
find "$BUILDROOT"/usr "$BUILDROOT"/etc -name '*.service' -type f -exec sed -i 's/^\(MemoryDeny\|SystemCall\)/# \1/' {} +

# 'systemd-hwdb update' takes > 50s when built with sanitizers so let's not run it by default.
systemctl --root="$BUILDROOT" mask systemd-hwdb-update.service

ASAN_RT_PATH="$(grep libasan.so < <(mkosi-chroot ldd "$LIBSYSTEMD") | cut -d ' ' -f 3)"
if [[ -z "$ASAN_RT_PATH" ]]; then
    ASAN_RT_PATH="$(grep libclang_rt.asan < <(mkosi-chroot ldd "$LIBSYSTEMD") | cut -d ' ' -f 3)"

    # As clang's ASan DSO is usually in a non-standard path, let's check if the RUNPATH is set accordingly.
    if mkosi-chroot ldd "$LIBSYSTEMD" | grep -q "libclang_rt.asan.*not found"; then
        echo >&2 "clang's ASan DSO libclang_rt.asan is not present in the runtime library path"
        exit 1
    fi
fi
if [[ -z "$ASAN_RT_PATH" ]]; then
    echo >&2 "systemd is not linked against the ASan DSO"
    echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
    exit 1
fi

wrap=(
    /usr/lib/polkit-1/polkitd
    /usr/libexec/polkit-1/polkitd
    agetty
    btrfs
    capsh
    chgrp
    chown
    cryptsetup
    curl
    dbus-broker-launch
    dbus-daemon
    delv
    dhcpd
    dig
    dmsetup
    dnsmasq
    findmnt
    getent
    getfacl
    id
    integritysetup
    iscsid
    kpartx
    logger
    login
    ls
    lsblk
    lvm
    mdadm
    mkfs.btrfs
    mkfs.erofs
    mkfs.ext4
    mkfs.vfat
    mkfs.xfs
    mksquashfs
    mkswap
    multipath
    multipathd
    nvme
    p11-kit
    pkill
    ps
    setfacl
    setpriv
    sshd
    stat
    su
    tar
    tgtd
    useradd
    userdel
    veritysetup
)

for bin in "${wrap[@]}"; do
    if ! mkosi-chroot command -v "$bin" >/dev/null; then
        continue
    fi

    if [[ "$bin" == getent ]]; then
        enable_lsan=1
    else
        enable_lsan=0
    fi

    target="$(mkosi-chroot command -v "$bin")"

    mv "$BUILDROOT/$target" "$BUILDROOT/$target.orig"

    cat >"$BUILDROOT/$target" <<EOF
#!/bin/bash
# Preload the ASan runtime DSO, otherwise ASAn will complain
export LD_PRELOAD="$ASAN_RT_PATH"
# Disable LSan to speed things up, since we don't care about leak reports
# from 'external' binaries
export ASAN_OPTIONS=detect_leaks=$enable_lsan
# Set argv[0] to the original binary name without the ".orig" suffix
exec -a "\$0" -- "${target}.orig" "\$@"
EOF
    chmod +x "$BUILDROOT/$target"
done

cat >"$BUILDROOT"/usr/lib/systemd/systemd-asan-env <<EOF
LD_PRELOAD=$ASAN_RT_PATH
LSAN_OPTIONS=detect_leaks=0
EOF