• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19520565317

19 Nov 2025 11:19PM UTC coverage: 72.548% (+0.1%) from 72.449%
19520565317

push

github

web-flow
core: Verify inherited FDs are writable for stdout/stderr (#39674)

When inheriting file descriptors for stdout/stderr (either from stdin or
when making stderr inherit from stdout), we previously just assumed they
would be writable and dup'd them. This could lead to broken setups if
the inherited FD was actually opened read-only.

Before dup'ing any inherited FDs to stdout/stderr, verify they are
actually writable using the new fd_is_writable() helper. If not, fall
back to /dev/null (or reopen the terminal in the TTY case) with a
warning, rather than silently creating a broken setup where output
operations would fail.

31 of 44 new or added lines in 3 files covered. (70.45%)

813 existing lines in 43 files now uncovered.

308541 of 425291 relevant lines covered (72.55%)

1188151.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.0
/src/basic/errno-util.h
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
#pragma once
3

4
#include <string.h>
5

6
#include "basic-forward.h"
7

8
/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */
9
#define ERRNO_BUF_LEN           1024
10

11
/* Note: the lifetime of the compound literal is the immediately surrounding block,
12
 * see C11 §6.5.2.5, and
13
 * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks
14
 *
15
 * Note that we use the GNU variant of strerror_r() here. */
16
#define STRERROR(errnum) strerror_r(ABS(errnum), (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN)
17

18
/* A helper to print an error message or message for functions that return 0 on EOF.
19
 * Note that we can't use ({ … }) to define a temporary variable, so errnum is
20
 * evaluated twice. */
21
#define STRERROR_OR_EOF(errnum) ((errnum) != 0 ? STRERROR(errnum) : "Unexpected EOF")
22

23
static inline void _reset_errno_(int *saved_errno) {
76,124,008✔
24
        if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */
76,124,008✔
25
                return;
26

27
        errno = *saved_errno;
76,121,245✔
28
}
29

30
#define PROTECT_ERRNO                           \
31
        _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
32

33
#define UNPROTECT_ERRNO                         \
34
        do {                                    \
35
                errno = _saved_errno_;          \
36
                _saved_errno_ = -1;             \
37
        } while (false)
38

39
#define LOCAL_ERRNO(value)                      \
40
        PROTECT_ERRNO;                          \
41
        errno = ABS(value)
42

43
#define return_with_errno(r, err)                     \
44
        do {                                          \
45
                errno = ABS(err);                     \
46
                return r;                             \
47
        } while (false)
48

49
static inline int negative_errno(void) {
2,787,166✔
50
        /* This helper should be used to shut up gcc if you know 'errno' is
51
         * negative. Instead of "return -errno;", use "return negative_errno();"
52
         * It will suppress bogus gcc warnings in case it assumes 'errno' might
53
         * be 0 and thus the caller's error-handling might not be triggered. */
54
        assert_return(errno > 0, -EINVAL);
2,787,166✔
55
        return -errno;
2,787,166✔
56
}
57

58
static inline int RET_NERRNO(int ret) {
40,065,932✔
59

60
        /* Helper to wrap system calls in to make them return negative errno errors. This brings system call
61
         * error handling in sync with how we usually handle errors in our own code, i.e. with immediate
62
         * returning of negative errno. Usage is like this:
63
         *
64
         *     …
65
         *     r = RET_NERRNO(unlink(t));
66
         *     …
67
         *
68
         * or
69
         *
70
         *     …
71
         *     fd = RET_NERRNO(open("/etc/fstab", O_RDONLY|O_CLOEXEC));
72
         *     …
73
         */
74

75
        if (ret < 0)
40,065,932✔
76
                return negative_errno();
2,787,105✔
77

78
        return ret;
79
}
80

81
/* Collect possible errors in <acc>, so that the first error can be returned.
82
 * Returns (possibly updated) <acc>. */
83
#define RET_GATHER(acc, err)                    \
84
        ({                                      \
85
                int *__a = &(acc), __e = (err); \
86
                if (*__a >= 0 && __e < 0)       \
87
                        *__a = __e;             \
88
                *__a;                           \
89
        })
90

91
static inline int errno_or_else(int fallback) {
69,757✔
92
        /* To be used when invoking library calls where errno handling is not defined clearly: we return
93
         * errno if it is set, and the specified error otherwise. The idea is that the caller initializes
94
         * errno to zero before doing an API call, and then uses this helper to retrieve a somewhat useful
95
         * error code */
96
        if (errno > 0)
69,757✔
97
                return -errno;
36✔
98

99
        return -ABS(fallback);
100
}
101

102
/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
103
#define _DEFINE_ABS_WRAPPER(name)                         \
104
        static inline bool ERRNO_IS_##name(intmax_t r) {  \
105
                if (r == INTMAX_MIN)                      \
106
                        return false;                     \
107
                return ERRNO_IS_NEG_##name(-ABS(r));      \
108
        }
109

110
/* For send()/recv() or read()/write(). */
111
static inline bool ERRNO_IS_NEG_TRANSIENT(intmax_t r) {
4,122,427✔
112
        return IN_SET(r,
4,122,188✔
113
                      -EAGAIN,
114
                      -EINTR);
115
}
116
_DEFINE_ABS_WRAPPER(TRANSIENT);
6,186✔
117

118
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
119
 *
120
 * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.  See the
121
 *          icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources.
122
 *
123
 * Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
124
 *          kernel tells us that with ETIMEDOUT, see tcp(7). */
125
static inline bool ERRNO_IS_NEG_DISCONNECT(intmax_t r) {
724,200✔
126
        return IN_SET(r,
724,200✔
127
                      -ECONNABORTED,
128
                      -ECONNREFUSED,
129
                      -ECONNRESET,
130
                      -EHOSTDOWN,
131
                      -EHOSTUNREACH,
132
                      -ENETDOWN,
133
                      -ENETRESET,
134
                      -ENETUNREACH,
135
                      -ENONET,
136
                      -ENOPROTOOPT,
137
                      -ENOTCONN,
138
                      -EPIPE,
139
                      -EPROTO,
140
                      -ESHUTDOWN,
141
                      -ETIMEDOUT);
142
}
143
_DEFINE_ABS_WRAPPER(DISCONNECT);
3,871✔
144

145
/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
146
 * the accept(2) man page. */
147
static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(intmax_t r) {
×
148
        return ERRNO_IS_NEG_DISCONNECT(r) ||
×
149
                ERRNO_IS_NEG_TRANSIENT(r) ||
×
150
                r == -EOPNOTSUPP;
151
}
152
_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
×
153

154
/* Resource exhaustion, could be our fault or general system trouble */
155
static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
1,326✔
156
        return IN_SET(r,
1,326✔
157
                      -EMFILE,
158
                      -ENFILE,
159
                      -ENOMEM);
160
}
UNCOV
161
_DEFINE_ABS_WRAPPER(RESOURCE);
×
162

163
/* Seven different errors for "operation/system call/socket feature not supported" */
164
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
385,710✔
165
        return IN_SET(r,
180,686✔
166
                      -EOPNOTSUPP,
167
                      -ENOTTY,
168
                      -ENOSYS,
169
                      -EAFNOSUPPORT,
170
                      -EPFNOSUPPORT,
171
                      -EPROTONOSUPPORT,
172
                      -ESOCKTNOSUPPORT,
173
                      -ENOPROTOOPT);
174
}
175
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
20,103✔
176

177
/* ioctl() with unsupported command/arg might additionally return EINVAL */
178
static inline bool ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(intmax_t r) {
199,440✔
179
        return ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EINVAL;
199,440✔
180
}
181
_DEFINE_ABS_WRAPPER(IOCTL_NOT_SUPPORTED);
197,955✔
182

183
/* Two different errors for access problems */
184
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
660,352✔
185
        return IN_SET(r,
656,994✔
186
                      -EACCES,
187
                      -EPERM);
188
}
189
_DEFINE_ABS_WRAPPER(PRIVILEGE);
3,419✔
190

191
/* Three different errors for writing on a filesystem */
192
static inline bool ERRNO_IS_NEG_FS_WRITE_REFUSED(intmax_t r) {
1,014✔
193
        return r == -EROFS || ERRNO_IS_NEG_PRIVILEGE(r);
5,649✔
194
}
195
_DEFINE_ABS_WRAPPER(FS_WRITE_REFUSED);
×
196

197
/* Three different errors for "not enough disk space" */
198
static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
×
199
        return IN_SET(r,
×
200
                      -ENOSPC,
201
                      -EDQUOT,
202
                      -EFBIG);
203
}
204
_DEFINE_ABS_WRAPPER(DISK_SPACE);
×
205

206
/* Three different errors for "this device does not quite exist" */
207
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
1,036,956✔
208
        return IN_SET(r,
1,037,166✔
209
                      -ENODEV,
210
                      -ENXIO,
211
                      -ENOENT);
212
}
213
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
49✔
214

215
/* Device is absent or "empty". We get -ENOMEDIUM from CD/DVD devices, also in VMs. */
216
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT_OR_EMPTY(intmax_t r) {
82,249✔
217
        return ERRNO_IS_NEG_DEVICE_ABSENT(r) ||
82,249✔
218
                r == -ENOMEDIUM;
219
}
220
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT_OR_EMPTY);
81,953✔
221

222
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
223
 * where it simply doesn't have the requested xattr the same way */
224
static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
54,407✔
225
        return r == -ENODATA ||
54,407✔
226
                ERRNO_IS_NEG_NOT_SUPPORTED(r);
54,407✔
227
}
228
_DEFINE_ABS_WRAPPER(XATTR_ABSENT);
43,784✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc