int getsourcefilter (int s, uint32_t interface, const struct sockaddr *group, socklen_t grouplen, uint32_t *fmode, uint32_t *numsrc, struct sockaddr_storage *slist) { /* We have to create an struct ip_msfilter object which we can pass to the kernel. */ socklen_t needed = GROUP_FILTER_SIZE (*numsrc); int use_alloca = __libc_use_alloca (needed); struct group_filter *gf; if (use_alloca) gf = (struct group_filter *) alloca (needed); else { gf = (struct group_filter *) malloc (needed); if (gf == NULL) return -1; } gf->gf_interface = interface; memcpy (&gf->gf_group, group, grouplen); gf->gf_numsrc = *numsrc; /* We need to provide the appropriate socket level value. */ int result; int sol = __get_sol (group->sa_family, grouplen); if (sol == -1) { __set_errno (EINVAL); result = -1; } else { result = __getsockopt (s, sol, MCAST_MSFILTER, gf, &needed); /* If successful, copy the results to the places the caller wants them in. */ if (result == 0) { *fmode = gf->gf_fmode; memcpy (slist, gf->gf_slist, MIN (*numsrc, gf->gf_numsrc) * sizeof (struct sockaddr_storage)); *numsrc = gf->gf_numsrc; } } if (! use_alloca) { int save_errno = errno; free (gf); __set_errno (save_errno); } return result; }
/* Write data pointed by the buffers described by VECTOR, which is a vector of COUNT 'struct iovec's, to file descriptor FD. The data is written in the order specified. Operates just like 'write' (see <unistd.h>) except that the data are taken from VECTOR instead of a contiguous buffer. */ ssize_t __libc_writev (int fd, const struct iovec *vector, int count) { /* Find the total number of bytes to be written. */ size_t bytes = 0; for (int i = 0; i < count; ++i) { /* Check for ssize_t overflow. */ if (SSIZE_MAX - bytes < vector[i].iov_len) { __set_errno (EINVAL); return -1; } bytes += vector[i].iov_len; } /* Allocate a temporary buffer to hold the data. We should normally use alloca since it's faster and does not require synchronization with other threads. But we cannot if the amount of memory required is too large. */ char *buffer; char *malloced_buffer = NULL; if (__libc_use_alloca (bytes)) buffer = (char *) __alloca (bytes); else { malloced_buffer = buffer = (char *) malloc (bytes); if (buffer == NULL) /* XXX I don't know whether it is acceptable to try writing the data in chunks. Probably not so we just fail here. */ return -1; } /* Copy the data into BUFFER. */ size_t to_copy = bytes; char *bp = buffer; for (int i = 0; i < count; ++i) { size_t copy = MIN (vector[i].iov_len, to_copy); bp = __mempcpy ((void *) bp, (void *) vector[i].iov_base, copy); to_copy -= copy; if (to_copy == 0) break; } ssize_t bytes_written = __write (fd, buffer, bytes); free(malloced_buffer); return bytes_written; }
/* Read data from file descriptor FD, and put the result in the buffers described by VECTOR, which is a vector of COUNT 'struct iovec's. The buffers are filled in the order specified. Operates just like 'read' (see <unistd.h>) except that data are put in VECTOR instead of a contiguous buffer. */ ssize_t __readv (int fd, const struct iovec *vector, int count) { /* Find the total number of bytes to be read. */ size_t bytes = 0; for (int i = 0; i < count; ++i) { /* Check for ssize_t overflow. */ if (SSIZE_MAX - bytes < vector[i].iov_len) { __set_errno (EINVAL); return -1; } bytes += vector[i].iov_len; } /* Allocate a temporary buffer to hold the data. We should normally use alloca since it's faster and does not require synchronization with other threads. But we cannot if the amount of memory required is too large. */ char *buffer; char *malloced_buffer __attribute__ ((__cleanup__ (ifree))) = NULL; if (__libc_use_alloca (bytes)) buffer = (char *) __alloca (bytes); else { malloced_buffer = buffer = (char *) malloc (bytes); if (buffer == NULL) return -1; } /* Read the data. */ ssize_t bytes_read = __read (fd, buffer, bytes); if (bytes_read < 0) return -1; /* Copy the data from BUFFER into the memory specified by VECTOR. */ bytes = bytes_read; for (int i = 0; i < count; ++i) { size_t copy = MIN (vector[i].iov_len, bytes); (void) memcpy ((void *) vector[i].iov_base, (void *) buffer, copy); buffer += copy; bytes -= copy; if (bytes == 0) break; } return bytes_read; }
enum nss_status _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); char *tmpbuf; enum nss_status status; ent_t intern = { true, false, false, NULL, {NULL, 0, 0} }; bool use_malloc = false; status = internal_setgrent (&intern); if (status != NSS_STATUS_SUCCESS) return status; tmpbuf = __alloca (buflen); do { while ((status = internal_getgrent_r (&intern, tmpbuf, buflen, user, group, start, size, groupsp, limit, errnop)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) if (__libc_use_alloca (buflen * 2)) tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen); else { buflen *= 2; char *newbuf = realloc (use_malloc ? tmpbuf : NULL, buflen); if (newbuf == NULL) { status = NSS_STATUS_TRYAGAIN; goto done; } use_malloc = true; tmpbuf = newbuf; } } while (status == NSS_STATUS_SUCCESS); status = NSS_STATUS_SUCCESS; done: if (use_malloc) free (tmpbuf); internal_endgrent (&intern); return status; }
int __get_nprocs () { /* XXX Here will come a test for the new system call. */ const size_t buffer_size = __libc_use_alloca (8192) ? 8192 : 512; char *buffer = alloca (buffer_size); char *buffer_end = buffer + buffer_size; char *cp = buffer_end; char *re = buffer_end; int result = 1; #ifdef O_CLOEXEC const int flags = O_RDONLY | O_CLOEXEC; #else const int flags = O_RDONLY; #endif /* The /proc/stat format is more uniform, use it by default. */ int fd = open_not_cancel_2 ("/proc/stat", flags); if (fd != -1) { result = 0; char *l; while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) /* The current format of /proc/stat has all the cpu* entries at the front. We assume here that stays this way. */ if (strncmp (l, "cpu", 3) != 0) break; else if (isdigit (l[3])) ++result; close_not_cancel_no_status (fd); } else { fd = open_not_cancel_2 ("/proc/cpuinfo", flags); if (fd != -1) { GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); close_not_cancel_no_status (fd); } } return result; }
int getipv4sourcefilter (int s, struct in_addr interface, struct in_addr group, uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist) { /* We have to create an struct ip_msfilter object which we can pass to the kernel. */ socklen_t needed = IP_MSFILTER_SIZE (*numsrc); int use_alloca = __libc_use_alloca (needed); struct ip_msfilter *imsf; if (use_alloca) imsf = (struct ip_msfilter *) alloca (needed); else { imsf = (struct ip_msfilter *) malloc (needed); if (imsf == NULL) return -1; } imsf->imsf_multiaddr = group; imsf->imsf_interface = interface; imsf->imsf_numsrc = *numsrc; int result = __getsockopt (s, SOL_IP, IP_MSFILTER, imsf, &needed); /* If successful, copy the results to the places the caller wants them in. */ if (result == 0) { *fmode = imsf->imsf_fmode; memcpy (slist, imsf->imsf_slist, MIN (*numsrc, imsf->imsf_numsrc) * sizeof (struct in_addr)); *numsrc = imsf->imsf_numsrc; } if (! use_alloca) { int save_errno = errno; free (imsf); __set_errno (save_errno); } return result; }
static int locked_vfxprintf (FILE *fp, const char *fmt, va_list ap, unsigned int mode_flags) { if (_IO_fwide (fp, 0) <= 0) return __vfprintf_internal (fp, fmt, ap, mode_flags); /* We must convert the narrow format string to a wide one. Each byte can produce at most one wide character. */ wchar_t *wfmt; mbstate_t mbstate; int res; int used_malloc = 0; size_t len = strlen (fmt) + 1; if (__glibc_unlikely (len > SIZE_MAX / sizeof (wchar_t))) { __set_errno (EOVERFLOW); return -1; } if (__libc_use_alloca (len * sizeof (wchar_t))) wfmt = alloca (len * sizeof (wchar_t)); else if ((wfmt = malloc (len * sizeof (wchar_t))) == NULL) return -1; else used_malloc = 1; memset (&mbstate, 0, sizeof mbstate); res = __mbsrtowcs (wfmt, &fmt, len, &mbstate); if (res != -1) res = __vfwprintf_internal (fp, wfmt, ap, mode_flags); if (used_malloc) free (wfmt); return res; }
/* Put STRING, which is of the form "NAME=VALUE", in the environment. */ int putenv (char *string) { const char *const name_end = strchr (string, '='); if (name_end != NULL) { char *name; #ifdef _LIBC int use_malloc = !__libc_use_alloca (name_end - string + 1); if (__builtin_expect (use_malloc, 0)) { name = strndup (string, name_end - string); if (name == NULL) return -1; } else name = strndupa (string, name_end - string); #else # define use_malloc 1 name = malloc (name_end - string + 1); if (name == NULL) return -1; memcpy (name, string, name_end - string); name[name_end - string] = '\0'; #endif int result = __add_to_environ (name, NULL, string, 1); if (__glibc_unlikely (use_malloc)) free (name); return result; } __unsetenv (string); return 0; }
int getipv4sourcefilter (int s, struct in_addr interface, struct in_addr group, uint32_t *fmode, uint32_t *numsrc, struct in_addr *slist) { /* The kernel assumes at least one in_addr. */ int imsf_len = IP_MSFILTER_SIZE (*numsrc ?: 1); int use_alloca = __libc_use_alloca (imsf_len); struct ip_msfilter *imsf; if (use_alloca) imsf = alloca (imsf_len); else { imsf = malloc (imsf_len); if (!imsf) return -1; } imsf->imsf_multiaddr = group; imsf->imsf_interface = interface; imsf->imsf_numsrc = *numsrc; int res = ioctl (s, SIOCGIPMSFILTER, imsf); if (res == -1) { if (!use_alloca) free (imsf); return -1; } *fmode = imsf->imsf_fmode; *numsrc = MIN (imsf->imsf_numsrc, *numsrc); memcpy (slist, imsf->imsf_slist, *numsrc * sizeof (struct in_addr)); if (!use_alloca) free (imsf); return 0; }
int __netlink_request (struct netlink_handle *h, int type) { struct netlink_res *nlm_next; struct netlink_res **new_nlm_list; static volatile size_t buf_size = 4096; char *buf; struct sockaddr_nl nladdr; struct nlmsghdr *nlmh; ssize_t read_len; bool done = false; bool use_malloc = false; if (__netlink_sendreq (h, type) < 0) return -1; size_t this_buf_size = buf_size; if (__libc_use_alloca (this_buf_size)) buf = alloca (this_buf_size); else { buf = malloc (this_buf_size); if (buf != NULL) use_malloc = true; else goto out_fail; } struct iovec iov = { buf, this_buf_size }; if (h->nlm_list != NULL) new_nlm_list = &h->end_ptr->next; else new_nlm_list = &h->nlm_list; while (! done) { struct msghdr msg = { (void *) &nladdr, sizeof (nladdr), &iov, 1, NULL, 0, 0 }; read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0)); if (read_len < 0) goto out_fail; if (nladdr.nl_pid != 0) continue; if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0)) { if (this_buf_size >= SIZE_MAX / 2) goto out_fail; nlm_next = *new_nlm_list; while (nlm_next != NULL) { struct netlink_res *tmpptr; tmpptr = nlm_next->next; free (nlm_next); nlm_next = tmpptr; } *new_nlm_list = NULL; if (__libc_use_alloca (2 * this_buf_size)) buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size); else { this_buf_size *= 2; char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size); if (new_buf == NULL) goto out_fail; new_buf = buf; use_malloc = true; } buf_size = this_buf_size; iov.iov_base = buf; iov.iov_len = this_buf_size; /* Increase sequence number, so that we can distinguish between old and new request messages. */ h->seq++; if (__netlink_sendreq (h, type) < 0) goto out_fail; continue; } size_t count = 0; size_t remaining_len = read_len; for (nlmh = (struct nlmsghdr *) buf; NLMSG_OK (nlmh, remaining_len); nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len)) { if ((pid_t) nlmh->nlmsg_pid != h->pid || nlmh->nlmsg_seq != h->seq) continue; ++count; if (nlmh->nlmsg_type == NLMSG_DONE) { /* We found the end, leave the loop. */ done = true; break; } if (nlmh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh); if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) errno = EIO; else errno = -nlerr->error; goto out_fail; } } /* If there was nothing with the expected nlmsg_pid and nlmsg_seq, there is no point to record it. */ if (count == 0) continue; nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res) + read_len); if (nlm_next == NULL) goto out_fail; nlm_next->next = NULL; nlm_next->nlh = memcpy (nlm_next + 1, buf, read_len); nlm_next->size = read_len; nlm_next->seq = h->seq; if (h->nlm_list == NULL) h->nlm_list = nlm_next; else h->end_ptr->next = nlm_next; h->end_ptr = nlm_next; } if (use_malloc) free (buf); return 0; out_fail: if (use_malloc) free (buf); return -1; }
/* This entry point is equivalent to the `crypt' function in Unix libcs. */ char * __md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen) { unsigned char alt_result[16] __attribute__ ((__aligned__ (__alignof__ (md5_uint32)))); size_t salt_len; size_t key_len; size_t cnt; char *cp; char *copied_key = NULL; char *copied_salt = NULL; char *free_key = NULL; size_t alloca_used = 0; /* Find beginning of salt string. The prefix should normally always be present. Just in case it is not. */ if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0) /* Skip salt prefix. */ salt += sizeof (md5_salt_prefix) - 1; salt_len = MIN (strcspn (salt, "$"), 8); key_len = strlen (key); if ((key - (char *) 0) % __alignof__ (md5_uint32) != 0) { char *tmp; if (__libc_use_alloca (alloca_used + key_len + __alignof__ (md5_uint32))) tmp = (char *) alloca (key_len + __alignof__ (md5_uint32)); else { free_key = tmp = (char *) malloc (key_len + __alignof__ (md5_uint32)); if (tmp == NULL) return NULL; } key = copied_key = memcpy (tmp + __alignof__ (md5_uint32) - (tmp - (char *) 0) % __alignof__ (md5_uint32), key, key_len); assert ((key - (char *) 0) % __alignof__ (md5_uint32) == 0); } if ((salt - (char *) 0) % __alignof__ (md5_uint32) != 0) { char *tmp = (char *) alloca (salt_len + __alignof__ (md5_uint32)); salt = copied_salt = memcpy (tmp + __alignof__ (md5_uint32) - (tmp - (char *) 0) % __alignof__ (md5_uint32), salt, salt_len); assert ((salt - (char *) 0) % __alignof__ (md5_uint32) == 0); } #ifdef USE_NSS /* Initialize libfreebl3. */ NSSLOWInitContext *nss_ictx = NSSLOW_Init (); if (nss_ictx == NULL) { free (free_key); return NULL; } NSSLOWHASHContext *nss_ctx = NULL; NSSLOWHASHContext *nss_alt_ctx = NULL; #else struct md5_ctx ctx; struct md5_ctx alt_ctx; #endif /* Prepare for the real work. */ md5_init_ctx (&ctx, nss_ctx); /* Add the key string. */ md5_process_bytes (key, key_len, &ctx, nss_ctx); /* Because the SALT argument need not always have the salt prefix we add it separately. */ md5_process_bytes (md5_salt_prefix, sizeof (md5_salt_prefix) - 1, &ctx, nss_ctx); /* The last part is the salt string. This must be at most 8 characters and it ends at the first `$' character (for compatibility with existing implementations). */ md5_process_bytes (salt, salt_len, &ctx, nss_ctx); /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ md5_init_ctx (&alt_ctx, nss_alt_ctx); /* Add key. */ md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Add salt. */ md5_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx); /* Add key again. */ md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Now get result of this (16 bytes) and add it to the other context. */ md5_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 16; cnt -= 16) md5_process_bytes (alt_result, 16, &ctx, nss_ctx); md5_process_bytes (alt_result, cnt, &ctx, nss_ctx); /* For the following code we need a NUL byte. */ *alt_result = '\0'; /* The original implementation now does something weird: for every 1 bit in the key the first 0 is added to the buffer, for every 0 bit the first character of the key. This does not seem to be what was intended but we have to follow this to be compatible. */ for (cnt = key_len; cnt > 0; cnt >>= 1) md5_process_bytes ((cnt & 1) != 0 ? (const void *) alt_result : (const void *) key, 1, &ctx, nss_ctx); /* Create intermediate result. */ md5_finish_ctx (&ctx, nss_ctx, alt_result); /* Now comes another weirdness. In fear of password crackers here comes a quite long loop which just processes the output of the previous round again. We cannot ignore this here. */ for (cnt = 0; cnt < 1000; ++cnt) { /* New context. */ md5_init_ctx (&ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) md5_process_bytes (key, key_len, &ctx, nss_ctx); else md5_process_bytes (alt_result, 16, &ctx, nss_ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) md5_process_bytes (salt, salt_len, &ctx, nss_ctx); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) md5_process_bytes (key, key_len, &ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) md5_process_bytes (alt_result, 16, &ctx, nss_ctx); else md5_process_bytes (key, key_len, &ctx, nss_ctx); /* Create intermediate result. */ md5_finish_ctx (&ctx, nss_ctx, alt_result); } #ifdef USE_NSS /* Free libfreebl3 resources. */ NSSLOW_Shutdown (nss_ictx); #endif /* Now we can construct the result string. It consists of three parts. */ cp = __stpncpy (buffer, md5_salt_prefix, MAX (0, buflen)); buflen -= sizeof (md5_salt_prefix) - 1; cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); buflen -= MIN ((size_t) MAX (0, buflen), salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } __b64_from_24bit (&cp, &buflen, alt_result[0], alt_result[6], alt_result[12], 4); __b64_from_24bit (&cp, &buflen, alt_result[1], alt_result[7], alt_result[13], 4); __b64_from_24bit (&cp, &buflen, alt_result[2], alt_result[8], alt_result[14], 4); __b64_from_24bit (&cp, &buflen, alt_result[3], alt_result[9], alt_result[15], 4); __b64_from_24bit (&cp, &buflen, alt_result[4], alt_result[10], alt_result[5], 4); __b64_from_24bit (&cp, &buflen, 0, 0, alt_result[11], 2); if (buflen <= 0) { __set_errno (ERANGE); buffer = NULL; } else *cp = '\0'; /* Terminate the string. */ /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. We do it in this way to clear correct_words[] inside the MD5 implementation as well. */ #ifndef USE_NSS __md5_init_ctx (&ctx); __md5_finish_ctx (&ctx, alt_result); explicit_bzero (&ctx, sizeof (ctx)); explicit_bzero (&alt_ctx, sizeof (alt_ctx)); #endif if (copied_key != NULL) explicit_bzero (copied_key, key_len); if (copied_salt != NULL) explicit_bzero (copied_salt, salt_len); free (free_key); return buffer; }
/* Get the next group from NSS (+ entry). If the NSS module supports initgroups_dyn, get all entries at once. */ static enum nss_status getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { enum nss_status status; struct group grpbuf; /* Try nss_initgroups_dyn if supported. We also need getgrgid_r. If this function is not supported, step through the whole group database with getgrent_r. */ if (! ent->skip_initgroups_dyn) { long int mystart = 0; long int mysize = limit <= 0 ? *size : limit; gid_t *mygroups = malloc (mysize * sizeof (gid_t)); if (mygroups == NULL) return NSS_STATUS_TRYAGAIN; /* For every gid in the list we get from the NSS module, get the whole group entry. We need to do this, since we need the group name to check if it is in the blacklist. In worst case, this is as twice as slow as stepping with getgrent_r through the whole group database. But for large group databases this is faster, since the user can only be in a limited number of groups. */ if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups, limit, errnop) == NSS_STATUS_SUCCESS) { status = NSS_STATUS_NOTFOUND; /* If there is no blacklist we can trust the underlying initgroups implementation. */ if (ent->blacklist.current <= 1) for (int i = 0; i < mystart; i++) add_group (start, size, groupsp, limit, mygroups[i]); else { /* A temporary buffer. We use the normal buffer, until we find an entry, for which this buffer is to small. In this case, we overwrite the pointer with one to a bigger buffer. */ char *tmpbuf = buffer; size_t tmplen = buflen; bool use_malloc = false; for (int i = 0; i < mystart; i++) { while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, tmpbuf, tmplen, errnop)) == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) { if (__libc_use_alloca (tmplen * 2)) { if (tmpbuf == buffer) { tmplen *= 2; tmpbuf = __alloca (tmplen); } else tmpbuf = extend_alloca (tmpbuf, tmplen, tmplen * 2); } else { tmplen *= 2; char *newbuf = realloc (use_malloc ? tmpbuf : NULL, tmplen); if (newbuf == NULL) { status = NSS_STATUS_TRYAGAIN; goto done; } use_malloc = true; tmpbuf = newbuf; } } if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1)) { if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0)) goto done; if (!in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent) && check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf)) { if (nss_setgrent != NULL) { nss_setgrent (1); ent->need_endgrent = true; } ent->skip_initgroups_dyn = true; goto iter; } } } status = NSS_STATUS_NOTFOUND; done: if (use_malloc) free (tmpbuf); } free (mygroups); return status; } free (mygroups); } /* If we come here, the NSS module does not support initgroups_dyn or we were confronted with a split group. In these cases we have to step through the whole list ourself. */ iter: do { if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) != NSS_STATUS_SUCCESS) break; } while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)); if (status == NSS_STATUS_SUCCESS) check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); return status; }
if (retval != -1) goto retry; } return retval; } int __nscd_innetgr (const char *netgroup, const char *host, const char *user, const char *domain) { size_t key_len = (strlen (netgroup) + strlen (host ?: "") + strlen (user ?: "") + strlen (domain ?: "") + 7); char *key; bool use_alloca = __libc_use_alloca (key_len); if (use_alloca) key = alloca (key_len); else { key = malloc (key_len); if (key == NULL) return -1; } char *wp = stpcpy (key, netgroup) + 1; if (host != NULL) { *wp++ = '\1'; wp = stpcpy (wp, host) + 1; } else
enum nss_status _nss_files_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { FILE *stream = fopen ("/etc/group", "rce"); if (stream == NULL) { *errnop = errno; return *errnop == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; } /* No other thread using this stream. */ __fsetlocking (stream, FSETLOCKING_BYCALLER); char *line = NULL; size_t linelen = 0; enum nss_status status = NSS_STATUS_SUCCESS; bool any = false; size_t buflen = 1024; void *buffer = alloca (buflen); bool buffer_use_malloc = false; gid_t *groups = *groupsp; /* We have to iterate over the entire file. */ while (1) { fpos_t pos; fgetpos (stream, &pos); ssize_t n = getline (&line, &linelen, stream); if (n < 0) { if (! feof_unlocked (stream)) status = ((*errnop = errno) == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL); break; } struct group grp; int res = _nss_files_parse_grent (line, &grp, buffer, buflen, errnop); if (res == -1) { size_t newbuflen = 2 * buflen; if (buffer_use_malloc || ! __libc_use_alloca (buflen + newbuflen)) { void *newbuf = realloc (buffer_use_malloc ? buffer : NULL, newbuflen); if (newbuf == NULL) { *errnop = ENOMEM; status = NSS_STATUS_TRYAGAIN; goto out; } buffer = newbuf; buflen = newbuflen; buffer_use_malloc = true; } else buffer = extend_alloca (buffer, buflen, newbuflen); /* Reread current line, the parser has clobbered it. */ fsetpos (stream, &pos); continue; } if (res > 0 && grp.gr_gid != group) for (char **m = grp.gr_mem; *m != NULL; ++m) if (strcmp (*m, user) == 0) { /* Matches user. Insert this group. */ if (*start == *size) { /* Need a bigger buffer. */ if (limit > 0 && *size == limit) /* We reached the maximum. */ goto out; long int newsize; if (limit <= 0) newsize = 2 * *size; else newsize = MIN (limit, 2 * *size); gid_t *newgroups = realloc (groups, newsize * sizeof (*groups)); if (newgroups == NULL) { *errnop = ENOMEM; status = NSS_STATUS_TRYAGAIN; goto out; } *groupsp = groups = newgroups; *size = newsize; } groups[*start] = grp.gr_gid; *start += 1; any = true; break; } } out: /* Free memory. */ if (buffer_use_malloc) free (buffer); free (line); fclose (stream); return status == NSS_STATUS_SUCCESS && !any ? NSS_STATUS_NOTFOUND : status; }
/* Execute FILE, searching in the `PATH' environment variable if it contains no slashes, with arguments ARGV and environment from ENVP. */ int __execvpe (const char *file, char *const argv[], char *const envp[]) { if (*file == '\0') { /* We check the simple case first. */ __set_errno (ENOENT); return -1; } if (strchr (file, '/') != NULL) { /* Don't search when it contains a slash. */ __execve (file, argv, envp); if (errno == ENOEXEC) { /* Count the arguments. */ int argc = 0; while (argv[argc++]) ; size_t len = (argc + 1) * sizeof (char *); char **script_argv; void *ptr = NULL; if (__libc_use_alloca (len)) script_argv = alloca (len); else script_argv = ptr = malloc (len); if (script_argv != NULL) { scripts_argv (file, argv, argc, script_argv); __execve (script_argv[0], script_argv, envp); free (ptr); } } } else { size_t pathlen; size_t alloclen = 0; char *path = getenv ("PATH"); if (path == NULL) { pathlen = confstr (_CS_PATH, (char *) NULL, 0); alloclen = pathlen + 1; } else pathlen = strlen (path); size_t len = strlen (file) + 1; alloclen += pathlen + len + 1; char *name; char *path_malloc = NULL; if (__libc_use_alloca (alloclen)) name = alloca (alloclen); else { path_malloc = name = malloc (alloclen); if (name == NULL) return -1; } if (path == NULL) { /* There is no `PATH' in the environment. The default search path is the current directory followed by the path `confstr' returns for `_CS_PATH'. */ path = name + pathlen + len + 1; path[0] = ':'; (void) confstr (_CS_PATH, path + 1, pathlen); } /* Copy the file name at the top. */ name = (char *) memcpy (name + pathlen + 1, file, len); /* And add the slash. */ *--name = '/'; char **script_argv = NULL; void *script_argv_malloc = NULL; bool got_eacces = false; char *p = path; do { char *startp; path = p; p = __strchrnul (path, ':'); if (p == path) /* Two adjacent colons, or a colon at the beginning or the end of `PATH' means to search the current directory. */ startp = name + 1; else startp = (char *) memcpy (name - (p - path), path, p - path); /* Try to execute this name. If it works, execve will not return. */ __execve (startp, argv, envp); if (errno == ENOEXEC) { if (script_argv == NULL) { /* Count the arguments. */ int argc = 0; while (argv[argc++]) ; size_t arglen = (argc + 1) * sizeof (char *); if (__libc_use_alloca (alloclen + arglen)) script_argv = alloca (arglen); else script_argv = script_argv_malloc = malloc (arglen); if (script_argv == NULL) { /* A possible EACCES error is not as important as the ENOMEM. */ got_eacces = false; break; } scripts_argv (startp, argv, argc, script_argv); } __execve (script_argv[0], script_argv, envp); } switch (errno) { case EACCES: /* Record the we got a `Permission denied' error. If we end up finding no executable we can use, we want to diagnose that we did find one but were denied access. */ got_eacces = true; case ENOENT: case ESTALE: case ENOTDIR: /* Those errors indicate the file is missing or not executable by us, in which case we want to just try the next path directory. */ case ENODEV: case ETIMEDOUT: /* Some strange filesystems like AFS return even stranger error numbers. They cannot reasonably mean anything else so ignore those, too. */ break; default: /* Some other error means we found an executable file, but something went wrong executing it; return the error to our caller. */ return -1; } } while (*p++ != '\0'); /* We tried every element and none of them worked. */ if (got_eacces) /* At least one failure was due to permissions, so report that error. */ __set_errno (EACCES); free (script_argv_malloc); free (path_malloc); } /* Return the error from the last attempt (probably ENOENT). */ return -1; }
char * __sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) { unsigned char alt_result[64] __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); unsigned char temp_result[64] __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); size_t salt_len; size_t key_len; size_t cnt; char *cp; char *copied_key = NULL; char *copied_salt = NULL; char *p_bytes; char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; bool rounds_custom = false; size_t alloca_used = 0; char *free_key = NULL; char *free_pbytes = NULL; /* Find beginning of salt string. The prefix should normally always be present. Just in case it is not. */ if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0) /* Skip salt prefix. */ salt += sizeof (sha512_salt_prefix) - 1; if (strncmp (salt, sha512_rounds_prefix, sizeof (sha512_rounds_prefix) - 1) == 0) { const char *num = salt + sizeof (sha512_rounds_prefix) - 1; char *endp; unsigned long int srounds = strtoul (num, &endp, 10); if (*endp == '$') { salt = endp + 1; rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); rounds_custom = true; } } salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); key_len = strlen (key); if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) { char *tmp; if (__libc_use_alloca (alloca_used + key_len + __alignof__ (uint64_t))) tmp = alloca_account (key_len + __alignof__ (uint64_t), alloca_used); else { free_key = tmp = (char *) malloc (key_len + __alignof__ (uint64_t)); if (tmp == NULL) return NULL; } key = copied_key = memcpy (tmp + __alignof__ (uint64_t) - (tmp - (char *) 0) % __alignof__ (uint64_t), key, key_len); assert ((key - (char *) 0) % __alignof__ (uint64_t) == 0); } if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) { char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t)); salt = copied_salt = memcpy (tmp + __alignof__ (uint64_t) - (tmp - (char *) 0) % __alignof__ (uint64_t), salt, salt_len); assert ((salt - (char *) 0) % __alignof__ (uint64_t) == 0); } #ifdef USE_NSS /* Initialize libfreebl3. */ NSSLOWInitContext *nss_ictx = NSSLOW_Init (); if (nss_ictx == NULL) { free (free_key); return NULL; } NSSLOWHASHContext *nss_ctx = NULL; NSSLOWHASHContext *nss_alt_ctx = NULL; #else struct sha512_ctx ctx; struct sha512_ctx alt_ctx; #endif /* Prepare for the real work. */ sha512_init_ctx (&ctx, nss_ctx); /* Add the key string. */ sha512_process_bytes (key, key_len, &ctx, nss_ctx); /* The last part is the salt string. This must be at most 16 characters and it ends at the first `$' character. */ sha512_process_bytes (salt, salt_len, &ctx, nss_ctx); /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ sha512_init_ctx (&alt_ctx, nss_alt_ctx); /* Add key. */ sha512_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Add salt. */ sha512_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx); /* Add key again. */ sha512_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Now get result of this (64 bytes) and add it to the other context. */ sha512_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 64; cnt -= 64) sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); sha512_process_bytes (alt_result, cnt, &ctx, nss_ctx); /* Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0 the key. */ for (cnt = key_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); else sha512_process_bytes (key, key_len, &ctx, nss_ctx); /* Create intermediate result. */ sha512_finish_ctx (&ctx, nss_ctx, alt_result); /* Start computation of P byte sequence. */ sha512_init_ctx (&alt_ctx, nss_alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < key_len; ++cnt) sha512_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx); /* Finish the digest. */ sha512_finish_ctx (&alt_ctx, nss_alt_ctx, temp_result); /* Create byte sequence P. */ if (__libc_use_alloca (alloca_used + key_len)) cp = p_bytes = (char *) alloca (key_len); else { free_pbytes = cp = p_bytes = (char *)malloc (key_len); if (free_pbytes == NULL) { free (free_key); return NULL; } } for (cnt = key_len; cnt >= 64; cnt -= 64) cp = mempcpy (cp, temp_result, 64); memcpy (cp, temp_result, cnt); /* Start computation of S byte sequence. */ sha512_init_ctx (&alt_ctx, nss_alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) sha512_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx); /* Finish the digest. */ sha512_finish_ctx (&alt_ctx, nss_alt_ctx, temp_result); /* Create byte sequence S. */ cp = s_bytes = alloca (salt_len); for (cnt = salt_len; cnt >= 64; cnt -= 64) cp = mempcpy (cp, temp_result, 64); memcpy (cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA512 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ sha512_init_ctx (&ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha512_process_bytes (p_bytes, key_len, &ctx, nss_ctx); else sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha512_process_bytes (s_bytes, salt_len, &ctx, nss_ctx); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha512_process_bytes (p_bytes, key_len, &ctx, nss_ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha512_process_bytes (alt_result, 64, &ctx, nss_ctx); else sha512_process_bytes (p_bytes, key_len, &ctx, nss_ctx); /* Create intermediate result. */ sha512_finish_ctx (&ctx, nss_ctx, alt_result); } #ifdef USE_NSS /* Free libfreebl3 resources. */ NSSLOW_Shutdown (nss_ictx); #endif /* Now we can construct the result string. It consists of three parts. */ cp = __stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen)); buflen -= sizeof (sha512_salt_prefix) - 1; if (rounds_custom) { int n = snprintf (cp, MAX (0, buflen), "%s%zu$", sha512_rounds_prefix, rounds); cp += n; buflen -= n; } cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); buflen -= MIN ((size_t) MAX (0, buflen), salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } __b64_from_24bit (&cp, &buflen, alt_result[0], alt_result[21], alt_result[42], 4); __b64_from_24bit (&cp, &buflen, alt_result[22], alt_result[43], alt_result[1], 4); __b64_from_24bit (&cp, &buflen, alt_result[44], alt_result[2], alt_result[23], 4); __b64_from_24bit (&cp, &buflen, alt_result[3], alt_result[24], alt_result[45], 4); __b64_from_24bit (&cp, &buflen, alt_result[25], alt_result[46], alt_result[4], 4); __b64_from_24bit (&cp, &buflen, alt_result[47], alt_result[5], alt_result[26], 4); __b64_from_24bit (&cp, &buflen, alt_result[6], alt_result[27], alt_result[48], 4); __b64_from_24bit (&cp, &buflen, alt_result[28], alt_result[49], alt_result[7], 4); __b64_from_24bit (&cp, &buflen, alt_result[50], alt_result[8], alt_result[29], 4); __b64_from_24bit (&cp, &buflen, alt_result[9], alt_result[30], alt_result[51], 4); __b64_from_24bit (&cp, &buflen, alt_result[31], alt_result[52], alt_result[10], 4); __b64_from_24bit (&cp, &buflen, alt_result[53], alt_result[11], alt_result[32], 4); __b64_from_24bit (&cp, &buflen, alt_result[12], alt_result[33], alt_result[54], 4); __b64_from_24bit (&cp, &buflen, alt_result[34], alt_result[55], alt_result[13], 4); __b64_from_24bit (&cp, &buflen, alt_result[56], alt_result[14], alt_result[35], 4); __b64_from_24bit (&cp, &buflen, alt_result[15], alt_result[36], alt_result[57], 4); __b64_from_24bit (&cp, &buflen, alt_result[37], alt_result[58], alt_result[16], 4); __b64_from_24bit (&cp, &buflen, alt_result[59], alt_result[17], alt_result[38], 4); __b64_from_24bit (&cp, &buflen, alt_result[18], alt_result[39], alt_result[60], 4); __b64_from_24bit (&cp, &buflen, alt_result[40], alt_result[61], alt_result[19], 4); __b64_from_24bit (&cp, &buflen, alt_result[62], alt_result[20], alt_result[41], 4); __b64_from_24bit (&cp, &buflen, 0, 0, alt_result[63], 2); if (buflen <= 0) { __set_errno (ERANGE); buffer = NULL; } else *cp = '\0'; /* Terminate the string. */ /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. We do it in this way to clear correct_words[] inside the SHA512 implementation as well. */ #ifndef USE_NSS __sha512_init_ctx (&ctx); __sha512_finish_ctx (&ctx, alt_result); memset (&ctx, '\0', sizeof (ctx)); memset (&alt_ctx, '\0', sizeof (alt_ctx)); #endif memset (temp_result, '\0', sizeof (temp_result)); memset (p_bytes, '\0', key_len); memset (s_bytes, '\0', salt_len); if (copied_key != NULL) memset (copied_key, '\0', key_len); if (copied_salt != NULL) memset (copied_salt, '\0', salt_len); free (free_key); free (free_pbytes); return buffer; }
int __get_nprocs (void) { static int cached_result = -1; static time_t timestamp; time_t now = time (NULL); time_t prev = timestamp; atomic_read_barrier (); if (now == prev && cached_result > -1) return cached_result; /* XXX Here will come a test for the new system call. */ const size_t buffer_size = __libc_use_alloca (8192) ? 8192 : 512; char *buffer = alloca (buffer_size); char *buffer_end = buffer + buffer_size; char *cp = buffer_end; char *re = buffer_end; const int flags = O_RDONLY | O_CLOEXEC; int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags); char *l; int result = 0; if (fd != -1) { l = next_line (fd, buffer, &cp, &re, buffer_end); if (l != NULL) do { char *endp; unsigned long int n = strtoul (l, &endp, 10); if (l == endp) { result = 0; break; } unsigned long int m = n; if (*endp == '-') { l = endp + 1; m = strtoul (l, &endp, 10); if (l == endp) { result = 0; break; } } result += m - n + 1; l = endp; while (l < re && isspace (*l)) ++l; } while (l < re); __close_nocancel_nostatus (fd); if (result > 0) goto out; } cp = buffer_end; re = buffer_end; /* Default to an SMP system in case we cannot obtain an accurate number. */ result = 2; /* The /proc/stat format is more uniform, use it by default. */ fd = __open_nocancel ("/proc/stat", flags); if (fd != -1) { result = 0; while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) /* The current format of /proc/stat has all the cpu* entries at the front. We assume here that stays this way. */ if (strncmp (l, "cpu", 3) != 0) break; else if (isdigit (l[3])) ++result; __close_nocancel_nostatus (fd); } else { fd = __open_nocancel ("/proc/cpuinfo", flags); if (fd != -1) { GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); __close_nocancel_nostatus (fd); } } out: cached_result = result; atomic_write_barrier (); timestamp = now; return result; }
/* Process the argument starting with d->__nextchar as a long option. d->optind should *not* have been advanced over this argument. If the value returned is -1, it was not actually a long option, the state is unchanged, and the argument should be processed as a set of short options (this can only happen when long_only is true). Otherwise, the option (and its argument, if any) have been consumed and the return value is the value to return from _getopt_internal_r. */ static int process_long_option (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int print_errors, const char *prefix) { char *nameend; size_t namelen; const struct option *p; const struct option *pfound = NULL; int n_options; int option_index; for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; namelen = nameend - d->__nextchar; /* First look for an exact match, counting the options as a side effect. */ for (p = longopts, n_options = 0; p->name; p++, n_options++) if (!strncmp (p->name, d->__nextchar, namelen) && namelen == strlen (p->name)) { /* Exact match found. */ pfound = p; option_index = n_options; break; } if (pfound == NULL) { /* Didn't find an exact match, so look for abbreviations. */ unsigned char *ambig_set = NULL; int ambig_malloced = 0; int ambig_fallback = 0; int indfound = -1; for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, d->__nextchar, namelen)) { if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) { /* Second or later nonexact match found. */ if (!ambig_fallback) { if (!print_errors) /* Don't waste effort tracking the ambig set if we're not going to print it anyway. */ ambig_fallback = 1; else if (!ambig_set) { if (__libc_use_alloca (n_options)) ambig_set = alloca (n_options); else if ((ambig_set = malloc (n_options)) == NULL) /* Fall back to simpler error message. */ ambig_fallback = 1; else ambig_malloced = 1; if (ambig_set) { memset (ambig_set, 0, n_options); ambig_set[indfound] = 1; } } if (ambig_set) ambig_set[option_index] = 1; } } } if (ambig_set || ambig_fallback) { if (print_errors) { if (ambig_fallback) fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"), argv[0], prefix, d->__nextchar); else { flockfile (stderr); fprintf (stderr, _("%s: option '%s%s' is ambiguous; possibilities:"), argv[0], prefix, d->__nextchar); for (option_index = 0; option_index < n_options; option_index++) if (ambig_set[option_index]) fprintf (stderr, " '%s%s'", prefix, longopts[option_index].name); /* This must use 'fprintf' even though it's only printing a single character, so that it goes through __fxprintf_nocancel when compiled as part of glibc. */ fprintf (stderr, "\n"); funlockfile (stderr); } } if (ambig_malloced) free (ambig_set); d->__nextchar += strlen (d->__nextchar); d->optind++; d->optopt = 0; return '?'; } option_index = indfound; } if (pfound == NULL) { /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. */ if (!long_only || argv[d->optind][1] == '-' || strchr (optstring, *d->__nextchar) == NULL) { if (print_errors) fprintf (stderr, _("%s: unrecognized option '%s%s'\n"), argv[0], prefix, d->__nextchar); d->__nextchar = NULL; d->optind++; d->optopt = 0; return '?'; } /* Otherwise interpret it as a short option. */ return -1; } /* We have found a matching long option. Consume it. */ d->optind++; d->__nextchar = NULL; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) d->optarg = nameend + 1; else { if (print_errors) fprintf (stderr, _("%s: option '%s%s' doesn't allow an argument\n"), argv[0], prefix, pfound->name); d->optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (d->optind < argc) d->optarg = argv[d->optind++]; else { if (print_errors) fprintf (stderr, _("%s: option '%s%s' requires an argument\n"), argv[0], prefix, pfound->name); d->optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; }
/* This function is used by `setenv' and `putenv'. The difference between the two functions is that for the former must create a new string which is then placed in the environment, while the argument of `putenv' must be used directly. This is all complicated by the fact that we try to reuse values once generated for a `setenv' call since we can never free the strings. */ int __add_to_environ (const char *name, const char *value, const char *combined, int replace) { char **ep; size_t size; /* Compute lengths before locking, so that the critical section is less of a performance bottleneck. VALLEN is needed only if COMBINED is null (unfortunately GCC is not smart enough to deduce this; see the #pragma at the start of this file). Testing COMBINED instead of VALUE causes setenv (..., NULL, ...) to dump core now instead of corrupting memory later. */ const size_t namelen = strlen (name); size_t vallen; if (combined == NULL) vallen = strlen (value) + 1; LOCK; /* We have to get the pointer now that we have the lock and not earlier since another thread might have created a new environment. */ ep = __environ; size = 0; if (ep != NULL) { for (; *ep != NULL; ++ep) if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=') break; else ++size; } if (ep == NULL || __builtin_expect (*ep == NULL, 1)) { char **new_environ; /* We allocated this space; we can extend it. */ new_environ = (char **) realloc (last_environ, (size + 2) * sizeof (char *)); if (new_environ == NULL) { UNLOCK; return -1; } if (__environ != last_environ) memcpy ((char *) new_environ, (char *) __environ, size * sizeof (char *)); new_environ[size] = NULL; new_environ[size + 1] = NULL; ep = new_environ + size; last_environ = __environ = new_environ; } if (*ep == NULL || replace) { char *np; /* Use the user string if given. */ if (combined != NULL) np = (char *) combined; else { const size_t varlen = namelen + 1 + vallen; #ifdef USE_TSEARCH char *new_value; int use_alloca = __libc_use_alloca (varlen); if (__builtin_expect (use_alloca, 1)) new_value = (char *) alloca (varlen); else { new_value = malloc (varlen); if (new_value == NULL) { UNLOCK; return -1; } } # ifdef _LIBC __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1), value, vallen); # else memcpy (new_value, name, namelen); new_value[namelen] = '='; memcpy (&new_value[namelen + 1], value, vallen); # endif np = KNOWN_VALUE (new_value); if (__glibc_likely (np == NULL)) #endif { #ifdef USE_TSEARCH if (__glibc_unlikely (! use_alloca)) np = new_value; else #endif { np = malloc (varlen); if (__glibc_unlikely (np == NULL)) { UNLOCK; return -1; } #ifdef USE_TSEARCH memcpy (np, new_value, varlen); #else memcpy (np, name, namelen); np[namelen] = '='; memcpy (&np[namelen + 1], value, vallen); #endif } /* And remember the value. */ STORE_VALUE (np); } #ifdef USE_TSEARCH else { if (__glibc_unlikely (! use_alloca)) free (new_value); } #endif } *ep = np; } UNLOCK; return 0; }