summaryrefslogtreecommitdiffstats
path: root/mkosi.sanitizers.chroot
blob: 524e3dadb19b5881f615408e6fabad9916a57455 (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
#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
set -o nounset

if [[ -z "${SANITIZERS:-}" ]]; 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.
mkdir -p /etc/systemd/system/systemd-journald.service.d
cat >/etc/systemd/system/systemd-journald.service.d/10-stdout-tty.conf <<EOF
[Service]
StandardOutput=kmsg
EOF

# ASAN and syscall filters aren't compatible with each other.
find /usr /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 mask systemd-hwdb-update.service

ASAN_RT_PATH="$(grep libasan.so < <(ldd /usr/lib/systemd/systemd) | cut -d ' ' -f 3)"
if [[ -z "$ASAN_RT_PATH" ]]; then
    ASAN_RT_PATH="$(grep libclang_rt.asan < <(ldd /usr/lib/systemd/systemd) | 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 ldd /usr/lib/systemd/systemd | 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 ! command -v "$bin" >/dev/null; then
        continue
    fi

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

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

    mv "$target" "$target.orig"

    cat >"$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 "$target"
done

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