Various fixes to streamline running code analysis tools such as clang-tidy (#37224)

This commit is contained in:
Daan De Meyer
2025-04-23 14:34:52 +02:00
committed by GitHub
25 changed files with 174 additions and 137 deletions
-54
View File
@@ -9,66 +9,12 @@
void log_set_assert_return_is_critical(bool b);
bool log_get_assert_return_is_critical(void) _pure_;
_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
_noreturn_ void log_assert_failed_unreachable(const char *file, int line, const char *func);
void log_assert_failed_return(const char *text, const char *file, int line, const char *func);
#ifdef __COVERITY__
/* Use special definitions of assertion macros in order to prevent
* false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
* for uses of assert_se() and assert_return().
*
* These definitions make expression go through a (trivial) function
* call to ensure they are not discarded. Also use ! or !! to ensure
* the boolean expressions are seen as such.
*
* This technique has been described and recommended in:
* https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
*/
extern void __coverity_panic__(void);
static inline void __coverity_check__(int condition) {
if (!condition)
__coverity_panic__();
}
static inline int __coverity_check_and_return__(int condition) {
return condition;
}
#define assert_message_se(expr, message) __coverity_check__(!!(expr))
#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
#else /* ! __COVERITY__ */
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
} while (false)
#define assert_log(expr, message) ((_likely_(expr)) \
? (true) \
: (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
#endif /* __COVERITY__ */
#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached() \
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
#define assert_return(expr, r) \
do { \
if (!assert_log(expr, #expr)) \
+10 -10
View File
@@ -6,12 +6,13 @@
#include <stdbool.h>
#include <stddef.h>
#include "memory-util.h"
#if HAVE_GCRYPT
#include <gcrypt.h>
#include "dlfcn-util.h"
#include "macro.h"
#include "memory-util.h"
extern DLSYM_PROTOTYPE(gcry_md_close);
extern DLSYM_PROTOTYPE(gcry_md_copy);
@@ -46,15 +47,6 @@ extern DLSYM_PROTOTYPE(gcry_strerror);
int initialize_libgcrypt(bool secmem);
static inline gcry_md_hd_t* sym_gcry_md_closep(gcry_md_hd_t *md) {
if (!md || !*md)
return NULL;
sym_gcry_md_close(*md);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL);
/* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */
#define sym_gcry_md_putc(h,c) \
do { \
@@ -63,4 +55,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL);
sym_gcry_md_write((h__), NULL, 0); \
(h__)->buf[(h__)->bufpos++] = (c) & 0xff; \
} while(false)
#else
typedef struct gcry_md_handle *gcry_md_hd_t;
static inline void sym_gcry_md_close(gcry_md_hd_t h) {
assert(h == NULL);
}
#endif
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, sym_gcry_md_close, NULL);
-3
View File
@@ -42,9 +42,6 @@
#error "neither int nor long are four bytes long?!?"
#endif
assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
static inline uint64_t u64_multiply_safe(uint64_t a, uint64_t b) {
if (_unlikely_(a != 0 && b > (UINT64_MAX / a)))
return 0; /* overflow */
+1
View File
@@ -92,6 +92,7 @@ endforeach
efi_conf.set_quoted('PROJECT_VERSION', project_major_version)
efi_conf.set_quoted('VERSION_TAG', version_tag)
efi_conf.set('PROJECT_URL', conf.get('PROJECT_URL'))
efi_conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
if meson.is_cross_build() and get_option('sbat-distro') == 'auto'
warning('Auto detection of SBAT information not supported when cross-building, disabling SBAT.')
+13
View File
@@ -13,6 +13,19 @@
/* Never try to read more than 16G into memory (and on 32bit 1G) */
#define FILE_READ_MAX MIN(SIZE_MAX/4, UINT64_C(16)*1024U*1024U*1024U)
void free(void *p) {
if (!p)
return;
/* Debugging an invalid free requires trace logging to find the call site or a debugger attached. For
* release builds it is not worth the bother to even warn when we cannot even print a call stack. */
#ifdef EFI_DEBUG
assert_se(BS->FreePool(p) == EFI_SUCCESS);
#else
(void) BS->FreePool(p);
#endif
}
void convert_efi_path(char16_t *path) {
assert(path);
+3 -12
View File
@@ -14,18 +14,9 @@
/* This is provided by the linker. */
extern uint8_t __executable_start[];
static inline void free(void *p) {
if (!p)
return;
/* Debugging an invalid free requires trace logging to find the call site or a debugger attached. For
* release builds it is not worth the bother to even warn when we cannot even print a call stack. */
#ifdef EFI_DEBUG
assert_se(BS->FreePool(p) == EFI_SUCCESS);
#else
(void) BS->FreePool(p);
#endif
}
DISABLE_WARNING_REDUNDANT_DECLS;
void free(void *p);
REENABLE_WARNING;
static inline void freep(void *p) {
free(*(void **) p);
+8 -9
View File
@@ -8,19 +8,12 @@
#include "fdset.h"
#include "memory-util.h"
typedef struct DynamicUser DynamicUser;
typedef struct Manager Manager;
typedef struct DynamicCreds {
/* A combination of a dynamic user and group */
DynamicUser *user;
DynamicUser *group;
} DynamicCreds;
/* Note that this object always allocates a pair of user and group under the same name, even if one of them isn't
* used. This means, if you want to allocate a group and user pair, and they might have two different names, then you
* need to allocated two of these objects. DynamicCreds below makes that easy. */
struct DynamicUser {
typedef struct DynamicUser {
Manager *manager;
unsigned n_ref;
@@ -29,7 +22,13 @@ struct DynamicUser {
int storage_socket[2];
char name[];
};
} DynamicUser;
typedef struct DynamicCreds {
/* A combination of a dynamic user and group */
DynamicUser *user;
DynamicUser *group;
} DynamicCreds;
int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds);
int dynamic_user_serialize_one(DynamicUser *d, const char *key, FILE *f, FDSet *fds);
-1
View File
@@ -27,7 +27,6 @@
#include "unit.h"
struct libmnt_monitor;
typedef struct Unit Unit;
/* Enforce upper limit how many names we allow */
#define MANAGER_MAX_NAMES 131072 /* 128K */
+57
View File
@@ -18,6 +18,63 @@
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
#endif
#define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
#else
_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
_noreturn_ void log_assert_failed_unreachable(const char *file, int line, const char *func);
#ifdef __COVERITY__
/* Use special definitions of assertion macros in order to prevent
* false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
* for uses of assert_se() and assert_return().
*
* These definitions make expression go through a (trivial) function
* call to ensure they are not discarded. Also use ! or !! to ensure
* the boolean expressions are seen as such.
*
* This technique has been described and recommended in:
* https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
*/
extern void __coverity_panic__(void);
static inline void __coverity_check__(int condition) {
if (!condition)
__coverity_panic__();
}
static inline int __coverity_check_and_return__(int condition) {
return condition;
}
#define assert_message_se(expr, message) __coverity_check__(!!(expr))
#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
#else /* ! __COVERITY__ */
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
} while (false)
#endif /* __COVERITY__ */
#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached() \
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
#endif
/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
+1 -1
View File
@@ -17,7 +17,7 @@ struct iovec {
};
DISABLE_WARNING_REDUNDANT_DECLS;
static inline void free(void *p);
void free(void *p);
REENABLE_WARNING;
#endif
+3
View File
@@ -463,3 +463,6 @@ assert_cc(sizeof(dummy_t) == 0);
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
+6
View File
@@ -23,6 +23,7 @@
#include "terminal-util.h"
#include "tmpfile-util.h"
#if HAVE_GCRYPT
static int format_key(
const void *seed,
size_t seed_size,
@@ -51,8 +52,10 @@ static int format_key(
return memstream_finalize(&m, ret, NULL);
}
#endif
int action_setup_keys(void) {
#if HAVE_GCRYPT
_cleanup_(unlink_and_freep) char *tmpfile = NULL;
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *path = NULL;
@@ -236,4 +239,7 @@ int action_setup_keys(void) {
#endif
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Forward-secure sealing not available.");
#endif
}
-12
View File
@@ -1,16 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#if HAVE_GCRYPT
int action_setup_keys(void);
#else
#include "log.h"
static inline int action_setup_keys(void) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Forward-secure sealing not available.");
}
#endif
+1 -4
View File
@@ -32,6 +32,7 @@ libjournal_core = static_library(
journalctl_sources = files(
'journalctl.c',
'journalctl-authenticate.c',
'journalctl-catalog.c',
'journalctl-filter.c',
'journalctl-misc.c',
@@ -40,10 +41,6 @@ journalctl_sources = files(
'journalctl-varlink.c',
)
if conf.get('HAVE_GCRYPT') == 1
journalctl_sources += files('journalctl-authenticate.c')
endif
if get_option('link-journalctl-shared')
journalctl_link_with = [libshared]
else
@@ -49,9 +49,9 @@ int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs) {
"Too small to be a router solicit, ignoring.");
const struct nd_router_solicit *a = (const struct nd_router_solicit*) rs->packet->raw_packet;
assert(a);
assert(a->nd_rs_type == ND_ROUTER_SOLICIT);
assert(a->nd_rs_code == 0);
assert_se(a);
assert_se(a->nd_rs_type == ND_ROUTER_SOLICIT);
assert_se(a->nd_rs_code == 0);
r = ndisc_parse_options(rs->packet, &rs->options);
if (r < 0)
+2 -7
View File
@@ -3,6 +3,8 @@
sd_journal_sources = files(
'sd-journal/audit-type.c',
'sd-journal/catalog.c',
'sd-journal/fsprg.c',
'sd-journal/journal-authenticate.c',
'sd-journal/journal-file.c',
'sd-journal/journal-send.c',
'sd-journal/journal-vacuum.c',
@@ -12,13 +14,6 @@ sd_journal_sources = files(
'sd-journal/sd-journal.c',
)
if conf.get('HAVE_GCRYPT') == 1
sd_journal_sources += files(
'sd-journal/fsprg.c',
'sd-journal/journal-authenticate.c',
)
endif
audit_type_includes = [config_h,
missing_audit_h,
'linux/audit.h']
+30
View File
@@ -26,6 +26,8 @@
#define RND_GEN_Q 0x02
#define RND_GEN_X 0x03
#if HAVE_GCRYPT
#pragma GCC diagnostic ignored "-Wpointer-arith"
/* TODO: remove void* arithmetic and this work-around */
@@ -201,6 +203,8 @@ static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq,
/******************************************************************************/
#endif
size_t FSPRG_mskinbytes(unsigned _secpar) {
VALIDATE_SECPAR(_secpar);
return 2 + 2 * (_secpar / 2) / 8; /* to store header,p,q */
@@ -216,6 +220,7 @@ size_t FSPRG_stateinbytes(unsigned _secpar) {
return 2 + 2 * _secpar / 8 + 8; /* to store header,n,x,epoch */
}
#if HAVE_GCRYPT
static void store_secpar(void *buf, uint16_t secpar) {
secpar = secpar / 16 - 1;
((uint8_t*) buf)[0] = (secpar >> 8) & 0xff;
@@ -229,8 +234,10 @@ static uint16_t read_secpar(const void *buf) {
(uint16_t)(((uint8_t*) buf)[1]) << 0;
return 16 * (secpar + 1);
}
#endif
int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
#if HAVE_GCRYPT
uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN];
gcry_mpi_t n, p, q;
uint16_t secpar;
@@ -273,9 +280,13 @@ int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned
sym_gcry_mpi_release(q);
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
#if HAVE_GCRYPT
gcry_mpi_t n, x;
uint16_t secpar;
int r;
@@ -296,9 +307,13 @@ int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedl
sym_gcry_mpi_release(x);
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int FSPRG_Evolve(void *state) {
#if HAVE_GCRYPT
gcry_mpi_t n, x;
uint16_t secpar;
uint64_t epoch;
@@ -323,15 +338,23 @@ int FSPRG_Evolve(void *state) {
sym_gcry_mpi_release(x);
return 0;
#else
return -EOPNOTSUPP;
#endif
}
uint64_t FSPRG_GetEpoch(const void *state) {
#if HAVE_GCRYPT
uint16_t secpar;
secpar = read_secpar(state + 0);
return uint64_import(state + 2 + 2 * secpar / 8, 8);
#else
return -EOPNOTSUPP;
#endif
}
int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
#if HAVE_GCRYPT
gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
uint16_t secpar;
int r;
@@ -374,9 +397,13 @@ int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, s
sym_gcry_mpi_release(xm);
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
#if HAVE_GCRYPT
uint16_t secpar;
int r;
@@ -388,4 +415,7 @@ int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);
return 0;
#else
return -EOPNOTSUPP;
#endif
}
@@ -25,6 +25,7 @@ static void* fssheader_free(FSSHeader *p) {
DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free);
#if HAVE_GCRYPT
static uint64_t journal_file_tag_seqnum(JournalFile *f) {
uint64_t r;
@@ -35,8 +36,10 @@ static uint64_t journal_file_tag_seqnum(JournalFile *f) {
return r;
}
#endif
int journal_file_append_tag(JournalFile *f) {
#if HAVE_GCRYPT
Object *o;
uint64_t p;
int r;
@@ -76,9 +79,13 @@ int journal_file_append_tag(JournalFile *f) {
f->hmac_running = false;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int journal_file_hmac_start(JournalFile *f) {
#if HAVE_GCRYPT
uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
gcry_error_t err;
int r;
@@ -107,6 +114,9 @@ int journal_file_hmac_start(JournalFile *f) {
f->hmac_running = true;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
@@ -252,6 +262,7 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
}
int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
#if HAVE_GCRYPT
int r;
assert(f);
@@ -307,9 +318,13 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin
}
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int journal_file_hmac_put_header(JournalFile *f) {
#if HAVE_GCRYPT
int r;
assert(f);
@@ -334,6 +349,9 @@ int journal_file_hmac_put_header(JournalFile *f) {
sym_gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int journal_file_fss_load(JournalFile *f) {
@@ -415,6 +433,7 @@ int journal_file_fss_load(JournalFile *f) {
}
int journal_file_hmac_setup(JournalFile *f) {
#if HAVE_GCRYPT
gcry_error_t e;
int r;
@@ -430,6 +449,9 @@ int journal_file_hmac_setup(JournalFile *f) {
return -EOPNOTSUPP;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int journal_file_append_first_tag(JournalFile *f) {
-2
View File
@@ -302,7 +302,6 @@ JournalFile* journal_file_close(JournalFile *f) {
free(f->compress_buffer);
#endif
#if HAVE_GCRYPT
if (f->fss_file) {
size_t sz = PAGE_ALIGN(f->fss_file_size);
assert(sz < SIZE_MAX);
@@ -314,7 +313,6 @@ JournalFile* journal_file_close(JournalFile *f) {
if (f->hmac)
sym_gcry_md_close(f->hmac);
#endif
return mfree(f);
}
+1 -6
View File
@@ -4,14 +4,11 @@
#include <inttypes.h>
#include <sys/uio.h>
#if HAVE_GCRYPT
# include <gcrypt.h>
#endif
#include "sd-event.h"
#include "sd-id128.h"
#include "compress.h"
#include "gcrypt-util.h"
#include "hashmap.h"
#include "journal-def.h"
#include "missing_fcntl.h"
@@ -106,7 +103,6 @@ typedef struct JournalFile {
void *compress_buffer;
#endif
#if HAVE_GCRYPT
gcry_md_hd_t hmac;
bool hmac_running;
@@ -121,7 +117,6 @@ typedef struct JournalFile {
void *fsprg_seed;
size_t fsprg_seed_size;
#endif
/* When we insert this file into the per-boot priority queue 'newest_by_boot_id' in sd_journal, then by these keys */
sd_id128_t newest_boot_id;
+2 -2
View File
@@ -2280,7 +2280,7 @@ static int verify_shutdown_creds(
* to catch. In any case, it also means that the payload guarded by
* these polkit calls should never be executed, and hence we should
* never reach this point. */
assert(!error_or_denial);
assert_se(!error_or_denial);
return 0;
}
@@ -3841,7 +3841,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
* to catch. In any case, it also means that the payload guarded by
* these polkit calls should never be executed, and hence we should
* never reach this point. */
assert(!error_or_denial);
assert_se(!error_or_denial);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PIDFD, &creds);
if (r < 0)
+1 -1
View File
@@ -4897,7 +4897,7 @@ static int tpm2_kdfe(
void *end = mempcpy(mempcpy(stpcpy(info, label) + 1, context_u, context_u_size), context_v, context_v_size);
/* assert we copied exactly the right amount that we allocated */
assert(end > info && (uintptr_t) end - (uintptr_t) info == info_len);
assert_se(end > info && (uintptr_t) end - (uintptr_t) info == info_len);
_cleanup_free_ void *buf = NULL;
r = kdf_ss_derive(
+1 -1
View File
@@ -19,7 +19,7 @@
/* This is a private header; never even think of including this directly! */
#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__)
#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__) && !defined(__clang_analyzer__)
# error "Do not include _sd-common.h directly; it is a private header."
#endif
+2 -2
View File
@@ -139,12 +139,12 @@ TEST(label_ops_post) {
fd = RET_NERRNO(get_dir_fd("label_test_dir", 0755));
text1 = "Add initial texts to file for testing label operations to file1\n";
assert(labelling_op(fd, text1, "file1.txt", 0644) == 0);
assert_se(labelling_op(fd, text1, "file1.txt", 0644) == 0);
assert_se(label_ops_post(fd, "file1.txt", true) == 0);
assert_se(strlen(text1) == (size_t)buf.st_size);
text2 = "Add text2 data to file2\n";
assert(labelling_op(fd, text2, "file2.txt", 0644) == 0);
assert_se(labelling_op(fd, text2, "file2.txt", 0644) == 0);
assert_se(label_ops_post(fd, "file2.txt", true) == 0);
assert_se(strlen(text2) == (size_t)buf.st_size);
assert_se(label_ops_post(fd, "file3.txt", true) == -ENOENT);
+7 -7
View File
@@ -300,19 +300,19 @@ TEST(pty_open_peer) {
_cleanup_free_ char *pty_path = NULL;
pty_fd = openpt_allocate(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK, &pty_path);
assert(pty_fd >= 0);
assert(pty_path);
assert_se(pty_fd >= 0);
assert_se(pty_path);
peer_fd = pty_open_peer(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
assert(peer_fd >= 0);
assert_se(peer_fd >= 0);
static const char x[] = { 'x', '\n' };
assert(write(pty_fd, x, sizeof(x)) == 2);
assert_se(write(pty_fd, x, sizeof(x)) == 2);
char buf[3];
assert(read(peer_fd, &buf, sizeof(buf)) == sizeof(x));
assert(buf[0] == x[0]);
assert(buf[1] == x[1]);
assert_se(read(peer_fd, &buf, sizeof(buf)) == sizeof(x));
assert_se(buf[0] == x[0]);
assert_se(buf[1] == x[1]);
}
TEST(terminal_new_session) {