2 * Copyright (c) 2021-2022 Sergey Sushilin <sergeysushilin@protonmail.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 #if defined(USE_SHADOW)
42 #define streq(s1, s2) (strcmp((s1), (s2)) == 0)
43 #define strneq(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
45 static inline __malloc
__alloc_size((1)) __returns_nonnull __warn_unused_result
void *xmalloc(size_t size
)
50 errc(EXIT_FAILURE
, EINVAL
, "xmalloc: zero-size allocation");
52 pointer
= malloc(size
);
55 err(EXIT_FAILURE
, "malloc: allocating %zu bytes", size
);
59 static inline __alloc_size((1, 2)) __returns_nonnull __warn_unused_result
void *xcalloc(size_t count
, size_t size
)
63 if (count
== 0 || size
== 0)
64 errc(EXIT_FAILURE
, EINVAL
, "xcalloc: zero-size allocation");
66 pointer
= calloc(count
, size
);
69 err(EXIT_FAILURE
, "calloc: allocating %zu * %zu bytes", count
, size
);
73 static inline __alloc_size((2)) __returns_nonnull __warn_unused_result
void *xrealloc(void *pointer
, size_t size
)
75 if (pointer
== NULL
&& size
== 0)
76 errc(EXIT_FAILURE
, EINVAL
, "realloc(NULL, 0)");
79 warnx("reallocating to zero size (use free() instead)");
81 * if (pointer == NULL)
82 * warnx("reallocating NULL to %zu (use malloc() instead)", size);
85 pointer
= realloc(pointer
, size
);
88 err(EXIT_FAILURE
, "realloc");
92 static inline __alloc_size((2, 3)) __returns_nonnull __warn_unused_result
void *xreallocarray(void *pointer
, size_t count
, size_t size
)
94 if (pointer
== NULL
&& (count
== 0 || size
== 0))
95 errc(EXIT_FAILURE
, EINVAL
, "reallocarray(NULL, 0)");
97 if (count
== 0 || size
== 0)
98 warnx("reallocating to zero size (use free() instead)");
100 * Do not warn since reallocarray(NULL, count, size) can be used
101 * for safe allocating memory with size equal to count * size.
103 * if (pointer == NULL)
104 * warnx("reallocating NULL to %zu (use malloc() instead)", size);
107 pointer
= reallocarray(pointer
, count
, size
);
110 err(EXIT_FAILURE
, "reallocarray");
114 static inline __nonnull((1)) void xfree(const void *pointer
)
116 void **p
= (void **)pointer
;
122 #define xfree(p) xfree(&p)
124 static inline __malloc
__alloc_size((2)) __returns_nonnull __warn_unused_result
__nonnull((1)) void *xmemdup(const void *pointer
, size_t size
)
126 return memcpy(xmalloc(size
), pointer
, size
);
128 static inline __malloc __returns_nonnull __warn_unused_result
__nonnull((1)) char *xstrdup(char const *string
)
130 return xmemdup(string
, strlen(string
) + 1);
132 static inline __malloc __returns_nonnull __warn_unused_result
__nonnull((1)) char *xstrndup(char const *string
, size_t size
)
134 char *s
= memchr(string
, '\0', size
);
139 s
= memcpy(xmalloc(size
+ 1), string
, size
);
145 static inline __warn_unused_result
__nonnull((1)) long long int safe_strtonum(char const *s
, long long int minval
, long long int maxval
, bool *succeed
)
147 char const *ep
= NULL
;
148 long long int num
= strtonum(s
, minval
, maxval
, &ep
);
151 warn("strtonum(%s): %s", s
, ep
);
154 *succeed
= (ep
== NULL
);
158 static inline __warn_unused_result
__nonnull((1)) unsigned long long int safe_strtounum(char const *s
, unsigned long long int maxval
, bool *succeed
)
160 char const *ep
= NULL
;
161 unsigned long long int num
= strtounum(s
, maxval
, &ep
);
164 warn("strtounum(%s): %s", s
, ep
);
167 *succeed
= (ep
== NULL
);
172 static inline __format_printf(2, 0) __nonnull((1, 2)) int xvfprintf(FILE *restrict fp
, char const *restrict fmt
, va_list ap
)
174 int n
= vfprintf(fp
, fmt
, ap
);
177 err(EXIT_FAILURE
, "vfprintf");
182 static inline __format_printf(2, 3) __nonnull((1, 2)) int xfprintf(FILE *restrict fp
, char const *restrict fmt
, ...)
187 int n
= xvfprintf(fp
, fmt
, ap
);
193 static inline __format_printf(2, 0) __nonnull((1, 2)) int xvsprintf(char *restrict buf
, char const *restrict fmt
, va_list ap
)
195 int n
= vsprintf(buf
, fmt
, ap
);
198 err(EXIT_FAILURE
, "vsprintf");
202 static inline __format_printf(2, 3) __nonnull((1, 2)) int xsprintf(char *restrict buf
, char const *restrict fmt
, ...)
208 n
= xvsprintf(buf
, fmt
, ap
);
214 static inline __format_printf(3, 0) __nonnull((1, 3)) int safe_vsnprintf(char *restrict buf
, size_t size
, char const *restrict fmt
, va_list ap
)
218 if (size
> INT_MAX
) {
219 warnc(EINVAL
, "vsnprintf");
223 n
= vsnprintf(buf
, size
, fmt
, ap
);
229 if ((size_t)n
>= size
) {
230 warnc(EOVERFLOW
, "vsnprintf");
236 static inline __format_printf(3, 4) __nonnull((1, 3)) int safe_snprintf(char *restrict buf
, size_t size
, char const *restrict fmt
, ...)
242 n
= safe_vsnprintf(buf
, size
, fmt
, ap
);
248 static inline __warn_unused_result
__format_printf(3, 0) __nonnull((1, 3)) int xvsnprintf(char *restrict buf
, size_t size
, char const *restrict fmt
, va_list ap
)
250 int n
= safe_vsnprintf(buf
, size
, fmt
, ap
);
257 static inline __format_printf(3, 4) __nonnull((1, 3)) int xsnprintf(char *restrict buf
, size_t size
, char const *restrict fmt
, ...)
263 n
= xvsnprintf(buf
, size
, fmt
, ap
);
269 static inline __nonnull((1)) void safe_pledge(char const *restrict promises
,
270 char const *const restrict
*restrict paths
)
272 /* Pledge is available only on OpenBSD. */
273 #if defined(__OpenBSD__)
274 if (pledge(promises
, paths
) < 0)
275 err(EXIT_FAILURE
, "pledge(%s)", promises
);
283 #pragma GCC poison pledge
285 static inline __nonnull((1)) int safe_open(char const *file
, int flags
, mode_t mode
)
287 int fd
= open(file
, flags
, mode
);
290 err(EXIT_FAILURE
, "open: %s", file
);
294 static inline void safe_close(int fd
)
297 err(EXIT_FAILURE
, "close");
300 static inline void safe_fstat(int fd
, struct stat
*sb
)
302 if (fstat(fd
, sb
) != 0)
303 err(EXIT_FAILURE
, "fstat");
307 * DATA -- Available information about user (uid or name),
308 * the XX entry we are searching for.
309 * STRUCT_SIZE -- the size of the struct keeping entry data.
310 * F -- pointer to reentrant function to access the entry data.
311 * NAME -- name of function F.
313 static inline void *safe_getxxyyy(const void *restrict data
,
316 char const *restrict name
,
317 char const *restrict error_format
)
318 __format_printf(5, 0) __nonnull((1, 3, 4, 5)) __warn_unused_result __malloc
;
319 static inline void *safe_getxxyyy(const void *restrict data
,
322 char const *restrict name
,
323 char const *restrict error_format
)
327 char *p
= xmalloc(struct_size
+ size
);
331 int (*g
)(const void *, void *, char *, size_t, void **) = f
;
332 s
= g(data
, p
, p
+ struct_size
, size
, &result
);
335 continue; /* Try again. */
338 if (__builtin_mul_overflow(2, size
, &size
))
339 errc(EXIT_FAILURE
, ENOMEM
, "%s", name
);
341 p
= xrealloc(p
, struct_size
+ size
);
345 /* We either got an error, or we succeeded and the
346 returned name fit in the buffer. */
355 } else if (result
== NULL
) {
357 /* Verify the error format string... Just for safety,
358 no more and yet no less. */
359 char *s
= strchr(error_format
, '%');
361 if (s
== NULL
|| (*(s
+ 1) != 's' && (*(s
+ 1) != 'l' && *(s
+ 2) != 'u')) || strchr(s
+ 1, '%') != NULL
)
362 errx(EXIT_FAILURE
, "wrong error format in getxxyyy_r: %s", error_format
);
365 warnx(error_format
, data
);
371 #define SAFE_GETXXNAM_GENERATE_STATIC(struct_name, function_name, error_format) \
372 static inline __malloc __nonnull((1)) struct struct_name *safe_##function_name(char const *name) \
374 return safe_getxxyyy(name, \
375 sizeof(struct struct_name), \
377 #function_name"_r", \
380 static inline __malloc __nonnull((1)) __returns_nonnull struct struct_name *x##function_name(char const *name) \
382 struct struct_name *p = safe_##function_name(name); \
384 exit(EXIT_FAILURE); \
387 #define SAFE_GETXXYID_GENERATE_STATIC(struct_name, function_name, xid_t, error_format) \
388 static inline __malloc struct struct_name *safe_##function_name(xid_t id) \
390 return safe_getxxyyy((void *)(uintptr_t)id, \
391 sizeof(struct struct_name), \
393 #function_name"_r", \
396 static inline __malloc __returns_nonnull struct struct_name *x##function_name(xid_t id) \
398 struct struct_name *p = safe_##function_name(id); \
400 exit(EXIT_FAILURE); \
404 SAFE_GETXXNAM_GENERATE_STATIC(passwd
, getpwnam
, "no matching password record was found for '%s' user");
405 SAFE_GETXXNAM_GENERATE_STATIC(group
, getgrnam
, "no matching group record was found for '%s' group");
406 SAFE_GETXXYID_GENERATE_STATIC(passwd
, getpwuid
, uid_t
, "no matching password record was found for user with %lu UID");
407 SAFE_GETXXYID_GENERATE_STATIC(group
, getgrgid
, gid_t
, "no matching group record was found for group with %lu GID");
410 SAFE_GETXXNAM_GENERATE_STATIC(spwd
, getspnam
, "no matching shadow record was found for '%s' user");
413 /* Poison all these functions to do not allow to use them in code below
414 instead of safe_getxxyyy functions. */
415 #pragma GCC poison getpwnam getgrnam getpwnam_r getgrnam_r
416 #pragma GCC poison getpwuid getgruid getpwuid_r getgrgid_r
418 # pragma GCC poison getspnam getspnam_r
421 #if defined(FLEX_SCANNER) || defined(YYBISON) || defined(YYBYACC)
422 # define malloc xmalloc
423 # define calloc xcalloc
424 # define realloc xrealloc
425 # define reallocarray xreallocarray
426 # define strdup xstrdup
427 # define strndup xstrndup
436 # pragma GCC poison malloc calloc realloc reallocarray strdup strndup free
439 #endif /* _WRAPPERS_H */