diff --git a/src/basic/util.h b/src/basic/util.h index f009d37d4c..dc33d66067 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -174,12 +174,21 @@ static inline void *mempset(void *s, int c, size_t n) { } static inline void _reset_errno_(int *saved_errno) { + if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ + return; + errno = *saved_errno; } #define PROTECT_ERRNO \ _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno +#define UNPROTECT_ERRNO \ + do { \ + errno = _saved_errno_; \ + _saved_errno_ = -1; \ + } while (false) + static inline int negative_errno(void) { /* This helper should be used to shut up gcc if you know 'errno' is * negative. Instead of "return -errno;", use "return negative_errno();" diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 5abc0c91bf..e491351dee 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -74,6 +74,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( } else { hn = gethostname_malloc(); if (!hn) { + UNPROTECT_ERRNO; *errnop = ENOMEM; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; @@ -96,6 +97,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( l = strlen(canonical); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2); if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -186,6 +188,8 @@ static enum nss_status fill_in_hostent( assert(errnop); assert(h_errnop); + PROTECT_ERRNO; + alen = FAMILY_ADDRESS_SIZE(af); for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++) @@ -202,6 +206,7 @@ static enum nss_status fill_in_hostent( (c > 0 ? c+1 : 2) * sizeof(char*); if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -321,6 +326,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( af = AF_INET; if (!IN_SET(af, AF_INET, AF_INET6)) { + UNPROTECT_ERRNO; *errnop = EAFNOSUPPORT; *h_errnop = NO_DATA; return NSS_STATUS_UNAVAIL; @@ -343,6 +349,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( } else { hn = gethostname_malloc(); if (!hn) { + UNPROTECT_ERRNO; *errnop = ENOMEM; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; @@ -362,6 +369,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r( local_address_ipv4 = LOCALADDRESS_IPV4; } + UNPROTECT_ERRNO; + return fill_in_hostent( canonical, additional, af, @@ -401,12 +410,14 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( assert(h_errnop); if (!IN_SET(af, AF_INET, AF_INET6)) { + UNPROTECT_ERRNO; *errnop = EAFNOSUPPORT; *h_errnop = NO_DATA; return NSS_STATUS_UNAVAIL; } if (len != FAMILY_ADDRESS_SIZE(af)) { + UNPROTECT_ERRNO; *errnop = EINVAL; *h_errnop = NO_RECOVERY; return NSS_STATUS_UNAVAIL; @@ -461,6 +472,7 @@ found: if (!canonical || additional_from_hostname) { hn = gethostname_malloc(); if (!hn) { + UNPROTECT_ERRNO; *errnop = ENOMEM; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; @@ -472,6 +484,7 @@ found: additional = hn; } + UNPROTECT_ERRNO; return fill_in_hostent( canonical, additional, af, diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 3d1fc28353..486a658958 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -153,6 +153,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( l = strlen(name); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -227,6 +228,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_DATA; return NSS_STATUS_UNAVAIL; @@ -313,6 +315,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -396,6 +399,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_DATA; return NSS_STATUS_UNAVAIL; @@ -484,6 +488,7 @@ enum nss_status _nss_mymachines_getpwnam_r( l = strlen(name); if (buflen < l+1) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -501,6 +506,7 @@ enum nss_status _nss_mymachines_getpwnam_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -564,6 +570,7 @@ enum nss_status _nss_mymachines_getpwuid_r( return NSS_STATUS_NOTFOUND; if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -579,6 +586,7 @@ enum nss_status _nss_mymachines_getpwuid_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -662,6 +670,7 @@ enum nss_status _nss_mymachines_getgrnam_r( l = sizeof(char*) + strlen(name) + 1; if (buflen < l) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -677,6 +686,7 @@ enum nss_status _nss_mymachines_getgrnam_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -740,12 +750,14 @@ enum nss_status _nss_mymachines_getgrgid_r( return NSS_STATUS_NOTFOUND; if (buflen < sizeof(char*) + 1) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } memzero(buffer, sizeof(char*)); if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -758,6 +770,7 @@ enum nss_status _nss_mymachines_getgrgid_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index a28b5d8ba8..8370fed076 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -186,6 +186,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( l = strlen(canonical); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -267,6 +268,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_RECOVERY; return ret; @@ -364,6 +366,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -455,6 +458,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_RECOVERY; return ret; @@ -492,12 +496,14 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( assert(h_errnop); if (!IN_SET(af, AF_INET, AF_INET6)) { + UNPROTECT_ERRNO; *errnop = EAFNOSUPPORT; *h_errnop = NO_DATA; return NSS_STATUS_UNAVAIL; } if (len != FAMILY_ADDRESS_SIZE(af)) { + UNPROTECT_ERRNO; *errnop = EINVAL; *h_errnop = NO_RECOVERY; return NSS_STATUS_UNAVAIL; @@ -576,6 +582,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ if (buflen < ms) { + UNPROTECT_ERRNO; *errnop = ERANGE; *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; @@ -636,6 +643,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_RECOVERY; return ret; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index f554828d49..f8db27ae27 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -210,6 +210,7 @@ enum nss_status _nss_systemd_getpwnam_r( l = strlen(name); if (buflen < l+1) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -227,6 +228,7 @@ enum nss_status _nss_systemd_getpwnam_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -310,6 +312,7 @@ enum nss_status _nss_systemd_getpwuid_r( l = strlen(translated) + 1; if (buflen < l) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -327,6 +330,7 @@ enum nss_status _nss_systemd_getpwuid_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -408,6 +412,7 @@ enum nss_status _nss_systemd_getgrnam_r( l = sizeof(char*) + strlen(name) + 1; if (buflen < l) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -423,6 +428,7 @@ enum nss_status _nss_systemd_getgrnam_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -506,6 +512,7 @@ enum nss_status _nss_systemd_getgrgid_r( l = sizeof(char*) + strlen(translated) + 1; if (buflen < l) { + UNPROTECT_ERRNO; *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -521,6 +528,7 @@ enum nss_status _nss_systemd_getgrgid_r( return NSS_STATUS_SUCCESS; fail: + UNPROTECT_ERRNO; *errnop = -r; return NSS_STATUS_UNAVAIL; } @@ -740,6 +748,7 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz LIST_FOREACH(entries, p, getpwent_data.position) { len = strlen(p->name) + 1; if (buflen < len) { + UNPROTECT_ERRNO; *errnop = ERANGE; ret = NSS_STATUS_TRYAGAIN; goto finalize; @@ -791,6 +800,7 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size LIST_FOREACH(entries, p, getgrent_data.position) { len = sizeof(char*) + strlen(p->name) + 1; if (buflen < len) { + UNPROTECT_ERRNO; *errnop = ERANGE; ret = NSS_STATUS_TRYAGAIN; goto finalize; diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index b3a4b1749c..e049abc4a4 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -361,11 +361,11 @@ static void test_unlink_noerrno(void) { { PROTECT_ERRNO; - errno = -42; + errno = 42; assert_se(unlink_noerrno(name) >= 0); - assert_se(errno == -42); + assert_se(errno == 42); assert_se(unlink_noerrno(name) < 0); - assert_se(errno == -42); + assert_se(errno == 42); } } diff --git a/src/test/test-util.c b/src/test/test-util.c index 40c1f4a3aa..ffacd65669 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -213,6 +213,30 @@ static void test_protect_errno(void) { assert_se(errno == 12); } +static void test_unprotect_errno_inner_function(void) { + PROTECT_ERRNO; + + errno = 2222; +} + +static void test_unprotect_errno(void) { + log_info("/* %s */", __func__); + + errno = 4711; + + PROTECT_ERRNO; + + errno = 815; + + UNPROTECT_ERRNO; + + assert_se(errno == 4711); + + test_unprotect_errno_inner_function(); + + assert_se(errno == 4711); +} + static void test_in_set(void) { log_info("/* %s */", __func__); @@ -383,6 +407,7 @@ int main(int argc, char *argv[]) { test_div_round_up(); test_u64log2(); test_protect_errno(); + test_unprotect_errno(); test_in_set(); test_log2i(); test_eqzero();