diff --git a/SOURCES/glibc-rh1032435.patch b/SOURCES/glibc-rh1032435.patch index ed5cc92..694bec2 100644 --- a/SOURCES/glibc-rh1032435.patch +++ b/SOURCES/glibc-rh1032435.patch @@ -17,11 +17,30 @@ Date: Wed Oct 30 16:13:37 2013 +0530 wrap around fgets that calls it multiple times with int sizes if necessary. -diff --git glibc-2.17-c758a686/nss/nss_files/files-XXX.c glibc-2.17-c758a686/nss/nss_files/files-XXX.c -index 082d1ea..b62208c 100644 ---- glibc-2.17-c758a686/nss/nss_files/files-XXX.c -+++ glibc-2.17-c758a686/nss/nss_files/files-XXX.c -@@ -179,8 +179,51 @@ CONCAT(_nss_files_end,ENTNAME) (void) +(The previous commit fixes upstream bug 16071.) + +commit ac60763eac3d43b7234dd21286ad3ec3f17957fc +Author: Andreas Schwab +Date: Mon Jun 23 10:24:45 2014 +0200 + + Don't ignore too long lines in nss_files (BZ #17079) + +commit e07aabba73ea62e7dfa0512507c92efb851fbdbe +Author: Florian Weimer +Date: Tue Sep 22 13:20:18 2015 +0200 + + Add test case for bug 18287 + +commit 90fa42a1d7b78de0d75f7e3af362275b2abe807f +Author: Florian Weimer +Date: Tue Sep 22 13:40:17 2015 +0200 + + Test in commit e07aabba73ea62e7dfa0512507c92efb851fbdbe is for bug 17079 + +diff -u b/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c +--- b/nss/nss_files/files-XXX.c ++++ b/nss/nss_files/files-XXX.c +@@ -179,8 +179,53 @@ return NSS_STATUS_SUCCESS; } @@ -45,10 +64,12 @@ index 082d1ea..b62208c 100644 + { + int curlen = ((remaining_len > (size_t) INT_MAX) ? INT_MAX + : remaining_len); -+ char *p = fgets_unlocked (curbuf, curlen, stream); + ++ /* Terminate the line so that we can test for overflow. */ + ((unsigned char *) curbuf)[curlen - 1] = 0xff; + ++ char *p = fgets_unlocked (curbuf, curlen, stream); ++ + /* EOF or read error. */ + if (p == NULL) + return gcr_error; @@ -74,7 +95,7 @@ index 082d1ea..b62208c 100644 static enum nss_status internal_getent (struct STRUCTURE *result, char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO -@@ -188,7 +231,7 @@ internal_getent (struct STRUCTURE *result, +@@ -188,7 +233,7 @@ { char *p; struct parser_data *data = (void *) buffer; @@ -83,7 +104,7 @@ index 082d1ea..b62208c 100644 int parse_result; if (buflen < sizeof *data + 2) -@@ -200,17 +243,16 @@ internal_getent (struct STRUCTURE *result, +@@ -200,17 +245,16 @@ do { @@ -105,7 +126,7 @@ index 082d1ea..b62208c 100644 { /* The line is too long. Give the user the opportunity to enlarge the buffer. */ -@@ -219,7 +261,8 @@ internal_getent (struct STRUCTURE *result, +@@ -219,7 +263,8 @@ return NSS_STATUS_TRYAGAIN; } @@ -115,3 +136,255 @@ index 082d1ea..b62208c 100644 while (isspace (*p)) ++p; } + +diff a/nss/bug17079.c b/nss/bug17079.c +--- /dev/null ++++ b/nss/bug17079.c +@@ -0,0 +1,236 @@ ++/* Test for bug 17079: heap overflow in NSS with small buffers. ++ Copyright (C) 2015 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Check if two passwd structs contain the same data. */ ++static bool ++equal (const struct passwd *a, const struct passwd *b) ++{ ++ return strcmp (a->pw_name, b->pw_name) == 0 ++ && strcmp (a->pw_passwd, b->pw_passwd) == 0 ++ && a->pw_uid == b->pw_uid ++ && a->pw_gid == b->pw_gid ++ && strcmp (a->pw_gecos, b->pw_gecos) == 0 ++ && strcmp (a->pw_dir, b->pw_dir) == 0 ++ && strcmp (a->pw_shell, b->pw_shell) == 0; ++} ++ ++enum { MAX_TEST_ITEMS = 10 }; ++static struct passwd test_items[MAX_TEST_ITEMS]; ++static int test_count; ++ ++/* Initialize test_items and test_count above, with data from the ++ passwd database. */ ++static bool ++init_test_items (void) ++{ ++ setpwent (); ++ do ++ { ++ struct passwd *pwd = getpwent (); ++ if (pwd == NULL) ++ break; ++ struct passwd *target = test_items + test_count; ++ target->pw_name = strdup (pwd->pw_name); ++ target->pw_passwd = strdup (pwd->pw_passwd); ++ target->pw_uid = pwd->pw_uid; ++ target->pw_gid = pwd->pw_gid; ++ target->pw_gecos = strdup (pwd->pw_gecos); ++ target->pw_dir = strdup (pwd->pw_dir); ++ target->pw_shell = strdup (pwd->pw_shell); ++ } ++ while (++test_count < MAX_TEST_ITEMS); ++ endpwent (); ++ ++ /* Filter out those test items which cannot be looked up by name or ++ UID. */ ++ bool found = false; ++ for (int i = 0; i < test_count; ++i) ++ { ++ struct passwd *pwd1 = getpwnam (test_items[i].pw_name); ++ struct passwd *pwd2 = getpwuid (test_items[i].pw_uid); ++ if (pwd1 == NULL || !equal (pwd1, test_items + i) ++ || pwd2 == NULL || !equal (pwd2, test_items + i)) ++ test_items[i].pw_name = NULL; ++ else ++ found = true; ++ } ++ ++ if (!found) ++ puts ("error: no accounts found which can be looked up by name and UID."); ++ return found; ++} ++ ++/* Set to true if an error is encountered. */ ++static bool errors; ++ ++/* Return true if the padding has not been tampered with. */ ++static bool ++check_padding (char *buffer, size_t size, char pad) ++{ ++ char *end = buffer + size; ++ while (buffer < end) ++ { ++ if (*buffer != pad) ++ return false; ++ ++buffer; ++ } ++ return true; ++} ++ ++/* Test one buffer size and padding combination. */ ++static void ++test_one (const struct passwd *item, size_t buffer_size, ++ char pad, size_t padding_size) ++{ ++ char *buffer = malloc (buffer_size + padding_size); ++ if (buffer == NULL) ++ { ++ puts ("error: malloc failure"); ++ errors = true; ++ return; ++ } ++ ++ struct passwd pwd; ++ struct passwd *result; ++ int ret; ++ ++ /* Test getpwname_r. */ ++ memset (buffer, pad, buffer_size + padding_size); ++ pwd = (struct passwd) {}; ++ ret = getpwnam_r (item->pw_name, &pwd, buffer, buffer_size, &result); ++ if (!check_padding (buffer + buffer_size, padding_size, pad)) ++ { ++ printf ("error: padding change: " ++ "name \"%s\", buffer size %zu, padding size %zu, pad 0x%02x\n", ++ item->pw_name, buffer_size, padding_size, (unsigned char) pad); ++ errors = true; ++ } ++ if (ret == 0) ++ { ++ if (result == NULL) ++ { ++ printf ("error: no data: name \"%s\", buffer size %zu\n", ++ item->pw_name, buffer_size); ++ errors = true; ++ } ++ else if (!equal (item, result)) ++ { ++ printf ("error: lookup mismatch: name \"%s\", buffer size %zu\n", ++ item->pw_name, buffer_size); ++ errors = true; ++ } ++ } ++ else if (ret != ERANGE) ++ { ++ errno = ret; ++ printf ("error: lookup failure for name \"%s\": %m (%d)\n", ++ item->pw_name, ret); ++ errors = true; ++ } ++ ++ /* Test getpwuid_r. */ ++ memset (buffer, pad, buffer_size + padding_size); ++ pwd = (struct passwd) {}; ++ ret = getpwuid_r (item->pw_uid, &pwd, buffer, buffer_size, &result); ++ if (!check_padding (buffer + buffer_size, padding_size, pad)) ++ { ++ printf ("error: padding change: " ++ "UID %ld, buffer size %zu, padding size %zu, pad 0x%02x\n", ++ (long) item->pw_uid, buffer_size, padding_size, ++ (unsigned char) pad); ++ errors = true; ++ } ++ if (ret == 0) ++ { ++ if (result == NULL) ++ { ++ printf ("error: no data: UID %ld, buffer size %zu\n", ++ (long) item->pw_uid, buffer_size); ++ errors = true; ++ } ++ else if (!equal (item, result)) ++ { ++ printf ("error: lookup mismatch: UID %ld, buffer size %zu\n", ++ (long) item->pw_uid, buffer_size); ++ errors = true; ++ } ++ } ++ else if (ret != ERANGE) ++ { ++ errno = ret; ++ printf ("error: lookup failure for UID \"%ld\": %m (%d)\n", ++ (long) item->pw_uid, ret); ++ errors = true; ++ } ++ ++ free (buffer); ++} ++ ++/* Test one buffer size with different paddings. */ ++static void ++test_buffer_size (size_t buffer_size) ++{ ++ for (int i = 0; i < test_count; ++i) ++ for (size_t padding_size = 0; padding_size < 3; ++padding_size) ++ { ++ test_one (test_items + i, buffer_size, '\0', padding_size); ++ if (padding_size > 0) ++ { ++ test_one (test_items + i, buffer_size, ':', padding_size); ++ test_one (test_items + i, buffer_size, '\n', padding_size); ++ test_one (test_items + i, buffer_size, '\xff', padding_size); ++ test_one (test_items + i, buffer_size, '@', padding_size); ++ } ++ } ++} ++ ++int ++do_test (void) ++{ ++ if (!init_test_items ()) ++ return 1; ++ printf ("info: %d test items\n", test_count); ++ ++ for (size_t buffer_size = 0; buffer_size <= 65; ++buffer_size) ++ test_buffer_size (buffer_size); ++ for (size_t buffer_size = 64 + 4; buffer_size < 256; buffer_size += 4) ++ test_buffer_size (buffer_size); ++ test_buffer_size (255); ++ test_buffer_size (257); ++ for (size_t buffer_size = 256; buffer_size < 512; buffer_size += 8) ++ test_buffer_size (buffer_size); ++ test_buffer_size (511); ++ test_buffer_size (513); ++ test_buffer_size (1024); ++ test_buffer_size (2048); ++ ++ if (errors) ++ return 1; ++ else ++ return 0; ++} ++ ++#define TEST_FUNCTION do_test () ++#include "../test-skeleton.c" +diff a/nss/Makefile b/nss/Makefile +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -39,6 +39,6 @@ + extra-objs += $(makedb-modules:=.o) + +-tests = test-netdb tst-nss-test1 ++tests = test-netdb tst-nss-test1 bug17079 + xtests = bug-erange + + include ../Makeconfig diff --git a/SOURCES/glibc-rh1165192.patch b/SOURCES/glibc-rh1165192.patch index 41e2591..488ac49 100644 --- a/SOURCES/glibc-rh1165192.patch +++ b/SOURCES/glibc-rh1165192.patch @@ -16,8 +16,8 @@ diff -up glibc-2.17-c758a686/nss/Makefile.rh1165192 glibc-2.17-c758a686/nss/Make makedb-modules = xmalloc hash-string extra-objs += $(makedb-modules:=.o) --tests = test-netdb tst-nss-test1 -+tests = test-netdb tst-nss-test1 tst-nss-getpwent +-tests = test-netdb tst-nss-test1 bug17079 ++tests = test-netdb tst-nss-test1 bug17079 tst-nss-getpwent xtests = bug-erange include ../Makeconfig diff --git a/SOURCES/glibc-rh1183545.patch b/SOURCES/glibc-rh1183545.patch index dbd87ab..a9ec106 100644 --- a/SOURCES/glibc-rh1183545.patch +++ b/SOURCES/glibc-rh1183545.patch @@ -12,8 +12,8 @@ index 449a258..553eafa 100644 makedb-modules = xmalloc hash-string extra-objs += $(makedb-modules:=.o) --tests = test-netdb tst-nss-test1 tst-nss-getpwent -+tests = test-netdb tst-nss-test1 tst-nss-getpwent \ +-tests = test-netdb tst-nss-test1 bug17079 tst-nss-getpwent ++tests = test-netdb tst-nss-test1 bug17079 tst-nss-getpwent \ + test-digits-dots xtests = bug-erange diff --git a/SOURCES/glibc-rh1249102.patch b/SOURCES/glibc-rh1249102.patch new file mode 100644 index 0000000..eb83b2e --- /dev/null +++ b/SOURCES/glibc-rh1249102.patch @@ -0,0 +1,26 @@ +commit a53fbd8e6cd2f69bdfa3431d616a5f332aea6664 +Author: Adhemerval Zanella +Date: Tue Jul 29 13:56:44 2014 -0500 + + PowerPC: Fix gprof entry point for LE + + This patch fixes the ELFv2 gprof entry point since the ABI + does not define function descriptors. It fixes BZ#17213. +diff --git a/sysdeps/powerpc/powerpc64/entry.h b/sysdeps/powerpc/powerpc64/entry.h +index 76ead1d..30553c1 100644 +--- a/sysdeps/powerpc/powerpc64/entry.h ++++ b/sysdeps/powerpc/powerpc64/entry.h +@@ -23,6 +23,7 @@ extern void _start (void); + + #define ENTRY_POINT _start + ++#if _CALL_ELF != 2 + /* We have to provide a special declaration. */ + #define ENTRY_POINT_DECL(class) class void _start (void); + +@@ -33,3 +34,4 @@ extern void _start (void); + #define TEXT_START \ + ({ extern unsigned long int _start_as_data[] asm ("_start"); \ + _start_as_data[0]; }) ++#endif + diff --git a/SOURCES/glibc-rh1284959-1.patch b/SOURCES/glibc-rh1284959-1.patch new file mode 100644 index 0000000..cdb1585 --- /dev/null +++ b/SOURCES/glibc-rh1284959-1.patch @@ -0,0 +1,238 @@ +Description: Makes trimming work consistently across arenas. +Author: Mel Gorman +Origin: git://sourceware.org/git/glibc.git +Bug-RHEL: N/A +Bug-Fedora: N/A +Bug-Upstream: #17195 +Upstream status: committed + +Part of commit 8a35c3fe122d49ba76dff815b3537affb5a50b45 is also included +to allow the use of ALIGN_UP within malloc/arena.c. + +commit c26efef9798914e208329c0e8c3c73bb1135d9e3 +Author: Mel Gorman +Date: Thu Apr 2 12:14:14 2015 +0530 + + malloc: Consistently apply trim_threshold to all heaps [BZ #17195] + + Trimming heaps is a balance between saving memory and the system overhead + required to update page tables and discard allocated pages. The malloc + option M_TRIM_THRESHOLD is a tunable that users are meant to use to decide + where this balance point is but it is only applied to the main arena. + + For scalability reasons, glibc malloc has per-thread heaps but these are + shrunk with madvise() if there is one page free at the top of the heap. + In some circumstances this can lead to high system overhead if a thread + has a control flow like + + while (data_to_process) { + buf = malloc(large_size); + do_stuff(); + free(buf); + } + + For a large size, the free() will call madvise (pagetable teardown, page + free and TLB flush) every time followed immediately by a malloc (fault, + kernel page alloc, zeroing and charge accounting). The kernel overhead + can dominate such a workload. + + This patch allows the user to tune when madvise gets called by applying + the trim threshold to the per-thread heaps and using similar logic to the + main arena when deciding whether to shrink. Alternatively if the dynamic + brk/mmap threshold gets adjusted then the new values will be obeyed by + the per-thread heaps. + + Bug 17195 was a test case motivated by a problem encountered in scientific + applications written in python that performance badly due to high page fault + overhead. The basic operation of such a program was posted by Julian Taylor + https://sourceware.org/ml/libc-alpha/2015-02/msg00373.html + + With this patch applied, the overhead is eliminated. All numbers in this + report are in seconds and were recorded by running Julian's program 30 + times. + + pyarray + glibc madvise + 2.21 v2 + System min 1.81 ( 0.00%) 0.00 (100.00%) + System mean 1.93 ( 0.00%) 0.02 ( 99.20%) + System stddev 0.06 ( 0.00%) 0.01 ( 88.99%) + System max 2.06 ( 0.00%) 0.03 ( 98.54%) + Elapsed min 3.26 ( 0.00%) 2.37 ( 27.30%) + Elapsed mean 3.39 ( 0.00%) 2.41 ( 28.84%) + Elapsed stddev 0.14 ( 0.00%) 0.02 ( 82.73%) + Elapsed max 4.05 ( 0.00%) 2.47 ( 39.01%) + + glibc madvise + 2.21 v2 + User 141.86 142.28 + System 57.94 0.60 + Elapsed 102.02 72.66 + + Note that almost a minutes worth of system time is eliminted and the + program completes 28% faster on average. + + To illustrate the problem without python this is a basic test-case for + the worst case scenario where every free is a madvise followed by a an alloc + + /* gcc bench-free.c -lpthread -o bench-free */ + static int num = 1024; + + void __attribute__((noinline,noclone)) dostuff (void *p) + { + } + + void *worker (void *data) + { + int i; + + for (i = num; i--;) + { + void *m = malloc (48*4096); + dostuff (m); + free (m); + } + + return NULL; + } + + int main() + { + int i; + pthread_t t; + void *ret; + if (pthread_create (&t, NULL, worker, NULL)) + exit (2); + if (pthread_join (t, &ret)) + exit (3); + return 0; + } + + Before the patch, this resulted in 1024 calls to madvise. With the patch applied, + madvise is called twice because the default trim threshold is high enough to avoid + this. + + This a more complex case where there is a mix of frees. It's simply a different worker + function for the test case above + + void *worker (void *data) + { + int i; + int j = 0; + void *free_index[num]; + + for (i = num; i--;) + { + void *m = malloc ((i % 58) *4096); + dostuff (m); + if (i % 2 == 0) { + free (m); + } else { + free_index[j++] = m; + } + } + for (; j >= 0; j--) + { + free(free_index[j]); + } + + return NULL; + } + + glibc 2.21 calls malloc 90305 times but with the patch applied, it's + called 13438. Increasing the trim threshold will decrease the number of + times it's called with the option of eliminating the overhead. + + ebizzy is meant to generate a workload resembling common web application + server workloads. It is threaded with a large working set that at its core + has an allocation, do_stuff, free loop that also hits this case. The primary + metric of the benchmark is records processed per second. This is running on + my desktop which is a single socket machine with an I7-4770 and 8 cores. + Each thread count was run for 30 seconds. It was only run once as the + performance difference is so high that the variation is insignificant. + + glibc 2.21 patch + threads 1 10230 44114 + threads 2 19153 84925 + threads 4 34295 134569 + threads 8 51007 183387 + + Note that the saving happens to be a concidence as the size allocated + by ebizzy was less than the default threshold. If a different number of + chunks were specified then it may also be necessary to tune the threshold + to compensate + + This is roughly quadrupling the performance of this benchmark. The difference in + system CPU usage illustrates why. + + ebizzy running 1 thread with glibc 2.21 + 10230 records/s 306904 + real 30.00 s + user 7.47 s + sys 22.49 s + + 22.49 seconds was spent in the kernel for a workload runinng 30 seconds. With the + patch applied + + ebizzy running 1 thread with patch applied + 44126 records/s 1323792 + real 30.00 s + user 29.97 s + sys 0.00 s + + system CPU usage was zero with the patch applied. strace shows that glibc + running this workload calls madvise approximately 9000 times a second. With + the patch applied madvise was called twice during the workload (or 0.06 + times per second). + + 2015-02-10 Mel Gorman + + [BZ #17195] + * malloc/arena.c (free): Apply trim threshold to per-thread heaps + as well as the main arena. + +Index: glibc-2.17-c758a686/malloc/arena.c +=================================================================== +--- glibc-2.17-c758a686.orig/malloc/arena.c ++++ glibc-2.17-c758a686/malloc/arena.c +@@ -661,7 +661,7 @@ heap_trim(heap_info *heap, size_t pad) + unsigned long pagesz = GLRO(dl_pagesize); + mchunkptr top_chunk = top(ar_ptr), p, bck, fwd; + heap_info *prev_heap; +- long new_size, top_size, extra, prev_size, misalign; ++ long new_size, top_size, top_area, extra, prev_size, misalign; + + /* Can this heap go away completely? */ + while(top_chunk == chunk_at_offset(heap, sizeof(*heap))) { +@@ -695,9 +695,16 @@ heap_trim(heap_info *heap, size_t pad) + set_head(top_chunk, new_size | PREV_INUSE); + /*check_chunk(ar_ptr, top_chunk);*/ + } ++ ++ /* Uses similar logic for per-thread arenas as the main arena with systrim ++ by preserving the top pad and at least a page. */ + top_size = chunksize(top_chunk); +- extra = (top_size - pad - MINSIZE - 1) & ~(pagesz - 1); +- if(extra < (long)pagesz) ++ top_area = top_size - MINSIZE - 1; ++ if (top_area <= pad) ++ return 0; ++ ++ extra = ALIGN_DOWN(top_area - pad, pagesz); ++ if ((unsigned long) extra < mp_.trim_threshold) + return 0; + /* Try to shrink. */ + if(shrink_heap(heap, extra) != 0) +Index: glibc-2.17-c758a686/malloc/malloc.c +=================================================================== +--- glibc-2.17-c758a686.orig/malloc/malloc.c ++++ glibc-2.17-c758a686/malloc/malloc.c +@@ -236,6 +236,8 @@ + /* For va_arg, va_start, va_end. */ + #include + ++/* For ALIGN_UP. */ ++#include + + /* + Debugging: diff --git a/SOURCES/glibc-rh1284959-2.patch b/SOURCES/glibc-rh1284959-2.patch new file mode 100644 index 0000000..9d45fb4 --- /dev/null +++ b/SOURCES/glibc-rh1284959-2.patch @@ -0,0 +1,63 @@ +Description: Make trimming logic consistent. +Author: Carlos O'Donell +Origin: git://sourceware.org/git/glibc.git +Bug-RHEL: N/A +Bug-Fedora: N/A +Bug-Upstream: #17195 +Upstream status: committed + +commit e4bc326dbbf7328775fe7dd39de1178821363e0a +Author: Carlos O'Donell +Date: Wed Oct 7 22:21:36 2015 -0400 + + malloc: Consistently apply trim_threshold to all heaps (Bug 17195) + + In the per-thread arenas we apply trim_threshold-based checks + to the extra space between the pad and the top_area. This isn't + quite accurate and instead we should be harmonizing with the way + in which trim_treshold is applied everywhere else like sysrtim + and _int_free. The trimming check should be based on the size of + the top chunk and only the size of the top chunk. The following + patch harmonizes the trimming and make it consistent for the main + arena and thread arenas. + + In the old code a large padding request might have meant that + trimming was not triggered. Now trimming is considered first based + on the chunk, then the pad is subtracted, and the remainder trimmed. + This is how all the other trimmings operate. I didn't measure the + performance difference of this change because it corrects what I + consider to be a behavioural anomaly. We'll need some profile driven + optimization to make this code better, and even there Ondrej and + others have better ideas on how to speedup malloc. + + Tested on x86_64 with no regressions. Already reviewed by Siddhesh + Poyarekar and Mel Gorman here and discussed here: + https://sourceware.org/ml/libc-alpha/2015-05/msg00002.html + +Index: glibc-2.17-c758a686/malloc/arena.c +=================================================================== +--- glibc-2.17-c758a686.orig/malloc/arena.c ++++ glibc-2.17-c758a686/malloc/arena.c +@@ -697,14 +697,20 @@ heap_trim(heap_info *heap, size_t pad) + } + + /* Uses similar logic for per-thread arenas as the main arena with systrim +- by preserving the top pad and at least a page. */ ++ and _int_free by preserving the top pad and rounding down to the nearest ++ page. */ + top_size = chunksize(top_chunk); ++ if ((unsigned long)(top_size) < ++ (unsigned long)(mp_.trim_threshold)) ++ return 0; ++ + top_area = top_size - MINSIZE - 1; + if (top_area <= pad) + return 0; + ++ /* Release in pagesize units and round down to the nearest page. */ + extra = ALIGN_DOWN(top_area - pad, pagesz); +- if ((unsigned long) extra < mp_.trim_threshold) ++ if (extra == 0) + return 0; + /* Try to shrink. */ + if(shrink_heap(heap, extra) != 0) diff --git a/SOURCES/glibc-rh1284959-3.patch b/SOURCES/glibc-rh1284959-3.patch new file mode 100644 index 0000000..4de0130 --- /dev/null +++ b/SOURCES/glibc-rh1284959-3.patch @@ -0,0 +1,52 @@ +Short description: Don't corrupt heap if top chunk is MINSIZE. +Author(s): Mel Gorman +Origin: git://sourceware.org/git/glibc.git +Bug-RHEL: N/A +Bug-Fedora: N/A +Bug-Upstream: #18502 +Upstream status: committed + +commit f8ef472c0ff4644445ec716036d31430b4fa4bab +Author: Mel Gorman +Date: Mon Jun 8 13:36:13 2015 +0100 + + malloc: Do not corrupt the top of a threaded heap if top chunk is MINSIZE [BZ #18502] + + mksquashfs was reported in openSUSE to be causing segmentation faults when + creating installation images. Testing showed that mksquashfs sometimes + failed and could be reproduced within 10 attempts. The core dump looked + like the heap top was corrupted and was pointing to an unmapped area. In + other cases, this has been due to an application corrupting glibc structures + but mksquashfs appears to be fine in this regard. + + The problem is that heap_trim is "growing" the top into unmapped space. + If the top chunk == MINSIZE then top_area is -1 and this check does not + behave as expected due to a signed/unsigned comparison + + if (top_area <= pad) + return 0; + + The next calculation extra = ALIGN_DOWN(top_area - pad, pagesz) calculates + extra as a negative number which also is unnoticed due to a signed/unsigned + comparison. We then call shrink_heap(heap, negative_number) which crashes + later. This patch adds a simple check against MINSIZE to make sure extra + does not become negative. It adds a cast to hint to the reader that this + is a signed vs unsigned issue. + + Without the patch, mksquash fails within 10 attempts. With it applied, it + completed 1000 times without error. The standard test suite "make check" + showed no changes in the summary of test results. + +Index: glibc-2.17-c758a686/malloc/arena.c +=================================================================== +--- glibc-2.17-c758a686.orig/malloc/arena.c ++++ glibc-2.17-c758a686/malloc/arena.c +@@ -705,7 +705,7 @@ heap_trim(heap_info *heap, size_t pad) + return 0; + + top_area = top_size - MINSIZE - 1; +- if (top_area <= pad) ++ if (top_area < 0 || (size_t) top_area <= pad) + return 0; + + /* Release in pagesize units and round down to the nearest page. */ diff --git a/SOURCES/glibc-rh1293976-2.patch b/SOURCES/glibc-rh1293976-2.patch new file mode 100644 index 0000000..e5656b7 --- /dev/null +++ b/SOURCES/glibc-rh1293976-2.patch @@ -0,0 +1,557 @@ +Short description: malloc: Test various special cases related to allocation failures +Author(s): Florian Weimer +Origin: git://sourceware.org/git/glibc.git +Bug-RHEL: #1296453 (rhel-7.2.z), #1293976 (rhel-7.3), #1256285 (SRT) +Bug-Fedora: NA +Bug-Upstream: NA +Upstream status: committed +# +# commit 1bd5483e104c8bde6e61dc5e3f8a848bc861872d +# Author: Florian Weimer +# Date: Tue Dec 29 20:32:35 2015 +0100 +# +# malloc: Test various special cases related to allocation failures +# +# This test case exercises unusual code paths in allocation functions, +# related to allocation failures. Specifically, the test can reveal +# the following bugs: +# +# (a) calloc returns non-zero memory on fallback to sysmalloc. +# (b) calloc can self-deadlock because it fails to release +# the arena lock on certain allocation failures. +# (c) pvalloc can dereference a NULL arena pointer. +# +# (a) and (b) appear specific to a faulty downstream backport. +# (c) was fixed as part of commit 10ad46bc6526edc5c7afcc57112da96917ff3629. +# +# The test for (a) was inspired by a reproducer supplied by Jeff Layton. +# +Index: glibc-2.17-c758a686/malloc/Makefile +=================================================================== +--- glibc-2.17-c758a686.orig/malloc/Makefile ++++ glibc-2.17-c758a686/malloc/Makefile +@@ -20,13 +20,16 @@ + # + subdir := malloc + ++include ../Makeconfig ++ + all: + + dist-headers := malloc.h + headers := $(dist-headers) obstack.h mcheck.h + tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \ + tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \ +- tst-malloc-usable tst-malloc-backtrace ++ tst-malloc-usable tst-malloc-backtrace \ ++ tst-malloc-thread-fail + test-srcs = tst-mtrace + + routines = malloc morecore mcheck mtrace obstack +@@ -43,6 +46,8 @@ libmemusage-inhibit-o = $(filter-out .os + + $(objpfx)tst-malloc-backtrace: $(common-objpfx)nptl/libpthread.so \ + $(common-objpfx)nptl/libpthread_nonshared.a ++$(objpfx)tst-malloc-thread-fail: $(common-objpfx)nptl/libpthread.so \ ++ $(common-objpfx)nptl/libpthread_nonshared.a + + # These should be removed by `make clean'. + extra-objs = mcheck-init.o libmcheck.a +@@ -50,8 +55,6 @@ extra-objs = mcheck-init.o libmcheck.a + # Include the cleanup handler. + aux := set-freeres thread-freeres + +-include ../Makeconfig +- + CPPFLAGS-memusagestat = -DNOT_IN_libc + + # The Perl script to analyze the output of the mtrace functions. +Index: glibc-2.17-c758a686/malloc/tst-malloc-thread-fail.c +=================================================================== +--- /dev/null ++++ glibc-2.17-c758a686/malloc/tst-malloc-thread-fail.c +@@ -0,0 +1,468 @@ ++/* Test allocation function behavior on allocation failure. ++ Copyright (C) 2015 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; see the file COPYING.LIB. If ++ not, see . */ ++ ++/* This test case attempts to trigger various unusual conditions ++ related to allocation failures, notably switching to a different ++ arena, and falling back to mmap (via sysmalloc). */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* This mirrors the C11 max_align_t type provided by GCC, but it is ++ also available in C99 mode. The aligned attributes are required ++ because some ABIs have reduced alignment requirements for struct ++ and union members. */ ++typedef struct { ++ long long ll __attribute__ ((__aligned__ (__alignof__ (long long)))); ++ long double ld __attribute__ ((__aligned__ (__alignof__ (long double)))); ++} libc_max_align_t; ++ ++/* Wrapper for calloc with an optimization barrier. */ ++static void * ++__attribute__ ((noinline, noclone)) ++allocate_zeroed (size_t a, size_t b) ++{ ++ return calloc (a, b); ++} ++ ++/* System page size, as determined by sysconf (_SC_PAGE_SIZE). */ ++static unsigned long page_size; ++ ++/* Test parameters. */ ++static size_t allocation_size; ++static size_t alignment; ++static enum { ++ with_malloc, ++ with_realloc, ++ with_aligned_alloc, ++ with_memalign, ++ with_posix_memalign, ++ with_valloc, ++ with_pvalloc, ++ with_calloc, ++ last_allocation_function = with_calloc ++} allocation_function; ++ ++/* True if an allocation function uses the alignment test ++ parameter. */ ++const static bool alignment_sensitive[last_allocation_function + 1] = ++ { ++ [with_aligned_alloc] = true, ++ [with_memalign] = true, ++ [with_posix_memalign] = true, ++ }; ++ ++/* Combined pointer/expected alignment result of an allocation ++ function. */ ++struct allocate_result { ++ void *pointer; ++ size_t alignment; ++}; ++ ++/* Call the allocation function specified by allocation_function, with ++ allocation_size and alignment (if applicable) as arguments. No ++ alignment check. */ ++static struct allocate_result ++allocate_1 (void) ++{ ++ switch (allocation_function) ++ { ++ case with_malloc: ++ return (struct allocate_result) ++ {malloc (allocation_size), __alignof__ (libc_max_align_t)}; ++ case with_realloc: ++ { ++ void *p = realloc (NULL, 16); ++ void *q; ++ if (p == NULL) ++ q = NULL; ++ else ++ { ++ q = realloc (p, allocation_size); ++ if (q == NULL) ++ free (p); ++ } ++ return (struct allocate_result) {q, __alignof__ (libc_max_align_t)}; ++ } ++ case with_aligned_alloc: ++ { ++ void *p = aligned_alloc (alignment, allocation_size); ++ return (struct allocate_result) {p, alignment}; ++ } ++ case with_memalign: ++ { ++ void *p = memalign (alignment, allocation_size); ++ return (struct allocate_result) {p, alignment}; ++ } ++ case with_posix_memalign: ++ { ++ void *p; ++ if (posix_memalign (&p, alignment, allocation_size)) ++ { ++ if (errno == ENOMEM) ++ p = NULL; ++ else ++ { ++ printf ("error: posix_memalign (p, %zu, %zu): %m\n", ++ alignment, allocation_size); ++ abort (); ++ } ++ } ++ return (struct allocate_result) {p, alignment}; ++ } ++ case with_valloc: ++ { ++ void *p = valloc (allocation_size); ++ return (struct allocate_result) {p, page_size}; ++ } ++ case with_pvalloc: ++ { ++ void *p = pvalloc (allocation_size); ++ return (struct allocate_result) {p, page_size}; ++ } ++ case with_calloc: ++ { ++ char *p = allocate_zeroed (1, allocation_size); ++ /* Check for non-zero bytes. */ ++ if (p != NULL) ++ for (size_t i = 0; i < allocation_size; ++i) ++ if (p[i] != 0) ++ { ++ printf ("error: non-zero byte at offset %zu\n", i); ++ abort (); ++ } ++ return (struct allocate_result) {p, __alignof__ (libc_max_align_t)}; ++ } ++ } ++ abort (); ++} ++ ++/* Call allocate_1 and perform the alignment check on the result. */ ++static void * ++allocate (void) ++{ ++ struct allocate_result r = allocate_1 (); ++#if __powerpc__ == 1 && __powerpc64__ == 0 ++ /* Sourceware bug 6527 on 32-bit POWER. ++ Ignore 16-byte alignment requirement when using malloc, realloc, or ++ calloc, since these functions are known not to provide enough ++ alignment. */ ++ if ((((uintptr_t) r.pointer) & (r.alignment - 1)) != 0 ++ && !(r.alignment == 16 ++ && (allocation_function == with_malloc ++ || allocation_function == with_realloc ++ || allocation_function == with_calloc))) ++#else ++ if ((((uintptr_t) r.pointer) & (r.alignment - 1)) != 0) ++#endif ++ { ++ printf ("error: allocation function %d, size %zu not aligned to %zu\n", ++ (int) allocation_function, allocation_size, r.alignment); ++ abort (); ++ } ++ return r.pointer; ++} ++ ++/* Barriers to synchronize thread creation and termination. */ ++static pthread_barrier_t start_barrier; ++static pthread_barrier_t end_barrier; ++ ++/* Thread function which performs the allocation test. Called by ++ pthread_create and from the main thread. */ ++static void * ++allocate_thread (void *closure) ++{ ++ /* Wait for the creation of all threads. */ ++ { ++ int ret = pthread_barrier_wait (&start_barrier); ++ if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) ++ { ++ errno = ret; ++ printf ("error: pthread_barrier_wait: %m\n"); ++ abort (); ++ } ++ } ++ ++ /* Allocate until we run out of memory, creating a single-linked ++ list. */ ++ struct list { ++ struct list *next; ++ }; ++ struct list *head = NULL; ++ while (true) ++ { ++ struct list *e = allocate (); ++ if (e == NULL) ++ break; ++ ++ e->next = head; ++ head = e; ++ } ++ ++ /* Wait for the allocation of all available memory. */ ++ { ++ int ret = pthread_barrier_wait (&end_barrier); ++ if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) ++ { ++ errno = ret; ++ printf ("error: pthread_barrier_wait: %m\n"); ++ abort (); ++ } ++ } ++ ++ /* Free the allocated memory. */ ++ while (head != NULL) ++ { ++ struct list *next = head->next; ++ free (head); ++ head = next; ++ } ++ ++ return NULL; ++} ++ ++/* Number of threads (plus the main thread. */ ++enum { thread_count = 8 }; ++ ++/* Thread attribute to request creation of threads with a non-default ++ stack size which is rather small. This avoids interfering with the ++ configured address space limit. */ ++static pthread_attr_t small_stack; ++ ++/* Runs one test in multiple threads, all in a subprocess so that ++ subsequent tests do not interfere with each other. */ ++static void ++run_one (void) ++{ ++ /* Isolate the tests in a subprocess, so that we can start over ++ from scratch. */ ++ pid_t pid = fork (); ++ if (pid == 0) ++ { ++ /* In the child process. Create the allocation threads. */ ++ pthread_t threads[thread_count]; ++ ++ for (unsigned i = 0; i < thread_count; ++i) ++ { ++ int ret = pthread_create (threads + i, &small_stack, allocate_thread, NULL); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_create: %m\n"); ++ abort (); ++ } ++ } ++ ++ /* Also run the test on the main thread. */ ++ allocate_thread (NULL); ++ ++ for (unsigned i = 0; i < thread_count; ++i) ++ { ++ int ret = pthread_join (threads[i], NULL); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_join: %m\n"); ++ abort (); ++ } ++ } ++ _exit (0); ++ } ++ else if (pid < 0) ++ { ++ printf ("error: fork: %m\n"); ++ abort (); ++ } ++ ++ /* In the parent process. Wait for the child process to exit. */ ++ int status; ++ if (waitpid (pid, &status, 0) < 0) ++ { ++ printf ("error: waitpid: %m\n"); ++ abort (); ++ } ++ if (status != 0) ++ { ++ printf ("error: exit status %d from child process\n", status); ++ exit (1); ++ } ++} ++ ++/* Run all applicable allocation functions for the current test ++ parameters. */ ++static void ++run_allocation_functions (void) ++{ ++ for (int af = 0; af <= last_allocation_function; ++af) ++ { ++ /* Run alignment-sensitive functions for non-default ++ alignments. */ ++ if (alignment_sensitive[af] != (alignment != 0)) ++ continue; ++ allocation_function = af; ++ run_one (); ++ } ++} ++ ++int ++do_test (void) ++{ ++ /* Limit the number of malloc arenas. We use a very low number so ++ that despute the address space limit configured below, all ++ requested arenas a can be created. */ ++ if (mallopt (M_ARENA_MAX, 2) == 0) ++ { ++ printf ("error: mallopt (M_ARENA_MAX) failed\n"); ++ return 1; ++ } ++ ++ /* Determine the page size. */ ++ { ++ long ret = sysconf (_SC_PAGE_SIZE); ++ if (ret < 0) ++ { ++ printf ("error: sysconf (_SC_PAGE_SIZE): %m\n"); ++ return 1; ++ } ++ page_size = ret; ++ } ++ ++ /* Limit the size of the process, so that memory allocation in ++ allocate_thread will eventually fail, without impacting the ++ entire system. */ ++ { ++ struct rlimit limit; ++ if (getrlimit (RLIMIT_AS, &limit) != 0) ++ { ++ printf ("getrlimit (RLIMIT_AS) failed: %m\n"); ++ return 1; ++ } ++ long target = 200 * 1024 * 1024; ++ if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target) ++ { ++ limit.rlim_cur = target; ++ if (setrlimit (RLIMIT_AS, &limit) != 0) ++ { ++ printf ("setrlimit (RLIMIT_AS) failed: %m\n"); ++ return 1; ++ } ++ } ++ } ++ ++ /* Initialize thread attribute with a reduced stack size. */ ++ { ++ int ret = pthread_attr_init (&small_stack); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_attr_init: %m\n"); ++ abort (); ++ } ++ unsigned long stack_size = ((256 * 1024) / page_size) * page_size; ++ if (stack_size < 4 * page_size) ++ stack_size = 8 * page_size; ++ ret = pthread_attr_setstacksize (&small_stack, stack_size); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_attr_setstacksize: %m\n"); ++ abort (); ++ } ++ } ++ ++ /* Initialize the barriers. We run thread_count threads, plus 1 for ++ the main thread. */ ++ { ++ int ret = pthread_barrier_init (&start_barrier, NULL, thread_count + 1); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_barrier_init: %m\n"); ++ abort (); ++ } ++ ++ ret = pthread_barrier_init (&end_barrier, NULL, thread_count + 1); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_barrier_init: %m\n"); ++ abort (); ++ } ++ } ++ ++ allocation_size = 144; ++ run_allocation_functions (); ++ allocation_size = page_size; ++ run_allocation_functions (); ++ ++ alignment = 128; ++ allocation_size = 512; ++ run_allocation_functions (); ++ ++ allocation_size = page_size; ++ run_allocation_functions (); ++ ++ allocation_size = 17 * page_size; ++ run_allocation_functions (); ++ ++ /* Deallocation the barriers and the thread attribute. */ ++ { ++ int ret = pthread_barrier_destroy (&end_barrier); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_barrier_destroy: %m\n"); ++ return 1; ++ } ++ ret = pthread_barrier_destroy (&start_barrier); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_barrier_destroy: %m\n"); ++ return 1; ++ } ++ ret = pthread_attr_destroy (&small_stack); ++ if (ret != 0) ++ { ++ errno = ret; ++ printf ("error: pthread_attr_destroy: %m\n"); ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++/* The repeated allocations take some time on slow machines. */ ++#define TIMEOUT 20 ++ ++/* No malloc perturbation should be used. We are testing that ++ default 0 perturbation values work. */ ++#define MALLOC_PERTURB 0 ++ ++#define TEST_FUNCTION do_test () ++#include "../test-skeleton.c" +Index: glibc-2.17-c758a686/test-skeleton.c +=================================================================== +--- glibc-2.17-c758a686.orig/test-skeleton.c ++++ glibc-2.17-c758a686/test-skeleton.c +@@ -233,7 +233,10 @@ main (int argc, char *argv[]) + pid_t termpid; + + /* Make uses of freed and uninitialized memory known. */ +- mallopt (M_PERTURB, 42); ++#ifndef MALLOC_PERTURB ++# define MALLOC_PERTURB 42 ++#endif ++ mallopt (M_PERTURB, MALLOC_PERTURB); + + #ifdef STDOUT_UNBUFFERED + setbuf (stdout, NULL); diff --git a/SOURCES/glibc-rh1293976.patch b/SOURCES/glibc-rh1293976.patch new file mode 100644 index 0000000..11331bb --- /dev/null +++ b/SOURCES/glibc-rh1293976.patch @@ -0,0 +1,140 @@ +Short description: CVE-2015-5220: calloc() returns non-zeroed memory. +Author(s): Ondrej Bilka +Origin: git://sourceware.org/git/glibc.git +Bug-RHEL: #1296453 (rhel-7.2.z), #1293976 (rhel-7.3), #1256285 (SRT) +Bug-Fedora: NA +Bug-Upstream: NA +Upstream status: committed +# +# commit e8349efd466cfedc0aa98be61d88ca8795c9e565 +# Author: Ondřej Bílka +# Date: Mon Dec 9 17:25:19 2013 +0100 +# +# Simplify perturb_byte logic. +# +diff --git a/malloc/malloc.c b/malloc/malloc.c +index 4821deb..ac8c3f6 100644 +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -1870,8 +1870,20 @@ static int check_action = DEFAULT_CHECK_ACTION; + + static int perturb_byte; + +-#define alloc_perturb(p, n) memset (p, (perturb_byte ^ 0xff) & 0xff, n) +-#define free_perturb(p, n) memset (p, perturb_byte & 0xff, n) ++static inline void ++alloc_perturb (char *p, size_t n) ++{ ++ if (__glibc_unlikely (perturb_byte)) ++ memset (p, perturb_byte ^ 0xff, n); ++} ++ ++static inline void ++free_perturb (char *p, size_t n) ++{ ++ if (__glibc_unlikely (perturb_byte)) ++ memset (p, perturb_byte, n); ++} ++ + + + #include +@@ -3287,8 +3299,7 @@ _int_malloc(mstate av, size_t bytes) + } + check_remalloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + } +@@ -3323,8 +3334,7 @@ _int_malloc(mstate av, size_t bytes) + victim->size |= NON_MAIN_ARENA; + check_malloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + } +@@ -3403,8 +3413,7 @@ _int_malloc(mstate av, size_t bytes) + + check_malloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + +@@ -3420,8 +3429,7 @@ _int_malloc(mstate av, size_t bytes) + victim->size |= NON_MAIN_ARENA; + check_malloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + +@@ -3545,8 +3553,7 @@ _int_malloc(mstate av, size_t bytes) + } + check_malloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + } +@@ -3649,8 +3656,7 @@ _int_malloc(mstate av, size_t bytes) + } + check_malloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + } +@@ -3684,8 +3690,7 @@ _int_malloc(mstate av, size_t bytes) + + check_malloced_chunk(av, victim, nb); + void *p = chunk2mem(victim); +- if (__builtin_expect (perturb_byte, 0)) +- alloc_perturb (p, bytes); ++ alloc_perturb (p, bytes); + return p; + } + +@@ -3705,7 +3710,7 @@ _int_malloc(mstate av, size_t bytes) + */ + else { + void *p = sysmalloc(nb, av); +- if (p != NULL && __builtin_expect (perturb_byte, 0)) ++ if (p != NULL) + alloc_perturb (p, bytes); + return p; + } +@@ -3798,8 +3803,7 @@ _int_free(mstate av, mchunkptr p, int have_lock) + } + } + +- if (__builtin_expect (perturb_byte, 0)) +- free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); ++ free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); + + set_fastchunks(av); + unsigned int idx = fastbin_index(size); +@@ -3881,8 +3885,7 @@ _int_free(mstate av, mchunkptr p, int have_lock) + goto errout; + } + +- if (__builtin_expect (perturb_byte, 0)) +- free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); ++ free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); + + /* consolidate backward */ + if (!prev_inuse(p)) { diff --git a/SOURCES/glibc-rh1296031-0.patch b/SOURCES/glibc-rh1296031-0.patch new file mode 100644 index 0000000..ed88f03 --- /dev/null +++ b/SOURCES/glibc-rh1296031-0.patch @@ -0,0 +1,461 @@ +Sourceware bug 16574 + +commit d668061994a7486a3ba9c7d5e7882d85a2883707 +Author: Andreas Schwab +Date: Thu Feb 13 11:01:57 2014 +0100 + + Fix memory leak in _nss_dns_gethostbyname4_r with big DNS answer + +commit ab7ac0f2cf8731fe4c3f3aea6088a7c0127b5725 +Author: Ondřej Bílka +Date: Sun Feb 16 12:59:23 2014 +0100 + + Deduplicate resolv/nss_dns/dns-host.c + + In resolv/nss_dns/dns-host.c one of code path duplicated code after + that. We merge these paths. + +commit ab09bf616ad527b249aca5f2a4956fd526f0712f +Author: Andreas Schwab +Date: Tue Feb 18 10:57:25 2014 +0100 + + Properly fix memory leak in _nss_dns_gethostbyname4_r with big DNS answer + + Instead of trying to guess whether the second buffer needs to be freed + set a flag at the place it is allocated + +Index: glibc-2.17-c758a686/include/resolv.h +=================================================================== +--- glibc-2.17-c758a686.orig/include/resolv.h ++++ glibc-2.17-c758a686/include/resolv.h +@@ -56,11 +56,11 @@ libc_hidden_proto (__res_randomid) + libc_hidden_proto (__res_state) + + int __libc_res_nquery (res_state, const char *, int, int, u_char *, int, +- u_char **, u_char **, int *, int *); ++ u_char **, u_char **, int *, int *, int *); + int __libc_res_nsearch (res_state, const char *, int, int, u_char *, int, +- u_char **, u_char **, int *, int *); ++ u_char **, u_char **, int *, int *, int *); + int __libc_res_nsend (res_state, const u_char *, int, const u_char *, int, +- u_char *, int, u_char **, u_char **, int *, int *) ++ u_char *, int, u_char **, u_char **, int *, int *, int *) + attribute_hidden; + + libresolv_hidden_proto (_sethtent) +Index: glibc-2.17-c758a686/resolv/gethnamaddr.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/gethnamaddr.c ++++ glibc-2.17-c758a686/resolv/gethnamaddr.c +@@ -616,7 +616,7 @@ gethostbyname2(name, af) + buf.buf = origbuf = (querybuf *) alloca (1024); + + if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024, +- &buf.ptr, NULL, NULL, NULL)) < 0) { ++ &buf.ptr, NULL, NULL, NULL, NULL)) < 0) { + if (buf.buf != origbuf) + free (buf.buf); + Dprintf("res_nsearch failed (%d)\n", n); +@@ -711,12 +711,12 @@ gethostbyaddr(addr, len, af) + buf.buf = orig_buf = (querybuf *) alloca (1024); + + n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024, +- &buf.ptr, NULL, NULL, NULL); ++ &buf.ptr, NULL, NULL, NULL, NULL); + if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) { + strcpy(qp, "ip6.int"); + n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, + buf.buf != orig_buf ? MAXPACKET : 1024, +- &buf.ptr, NULL, NULL, NULL); ++ &buf.ptr, NULL, NULL, NULL, NULL); + } + if (n < 0) { + if (buf.buf != orig_buf) +Index: glibc-2.17-c758a686/resolv/nss_dns/dns-canon.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/nss_dns/dns-canon.c ++++ glibc-2.17-c758a686/resolv/nss_dns/dns-canon.c +@@ -61,7 +61,7 @@ _nss_dns_getcanonname_r (const char *nam + { + int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i], + buf, sizeof (buf), &ansp.ptr, NULL, NULL, +- NULL); ++ NULL, NULL); + if (r > 0) + { + /* We need to decode the response. Just one question record. +Index: glibc-2.17-c758a686/resolv/nss_dns/dns-host.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/nss_dns/dns-host.c ++++ glibc-2.17-c758a686/resolv/nss_dns/dns-host.c +@@ -190,7 +190,7 @@ _nss_dns_gethostbyname3_r (const char *n + host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); + + n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf, +- 1024, &host_buffer.ptr, NULL, NULL, NULL); ++ 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL); + if (n < 0) + { + switch (errno) +@@ -225,7 +225,7 @@ _nss_dns_gethostbyname3_r (const char *n + n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf, + host_buffer.buf != orig_host_buffer + ? MAXPACKET : 1024, &host_buffer.ptr, +- NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL); + + if (n < 0) + { +@@ -308,13 +308,20 @@ _nss_dns_gethostbyname4_r (const char *n + u_char *ans2p = NULL; + int nans2p = 0; + int resplen2 = 0; ++ int ans2p_malloced = 0; + + int olderr = errno; + enum nss_status status; + int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC, + host_buffer.buf->buf, 2048, &host_buffer.ptr, +- &ans2p, &nans2p, &resplen2); +- if (n < 0) ++ &ans2p, &nans2p, &resplen2, &ans2p_malloced); ++ if (n >= 0) ++ { ++ status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p, ++ resplen2, name, pat, buffer, buflen, ++ errnop, herrnop, ttlp); ++ } ++ else + { + switch (errno) + { +@@ -341,16 +348,11 @@ _nss_dns_gethostbyname4_r (const char *n + *errnop = EAGAIN; + else + __set_errno (olderr); +- +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); +- +- return status; + } + +- status = gaih_getanswer(host_buffer.buf, n, (const querybuf *) ans2p, +- resplen2, name, pat, buffer, buflen, +- errnop, herrnop, ttlp); ++ /* Check whether ans2p was separately allocated. */ ++ if (ans2p_malloced) ++ free (ans2p); + + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); +@@ -460,7 +462,7 @@ _nss_dns_gethostbyaddr2_r (const void *a + strcpy (qp, "].ip6.arpa"); + n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, + host_buffer.buf->buf, 1024, &host_buffer.ptr, +- NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL); + if (n >= 0) + goto got_it_already; + } +@@ -481,14 +483,14 @@ _nss_dns_gethostbyaddr2_r (const void *a + } + + n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, +- 1024, &host_buffer.ptr, NULL, NULL, NULL); ++ 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL); + if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) + { + strcpy (qp, "ip6.int"); + n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, + host_buffer.buf != orig_host_buffer + ? MAXPACKET : 1024, &host_buffer.ptr, +- NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL); + } + if (n < 0) + { +Index: glibc-2.17-c758a686/resolv/nss_dns/dns-network.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/nss_dns/dns-network.c ++++ glibc-2.17-c758a686/resolv/nss_dns/dns-network.c +@@ -129,7 +129,7 @@ _nss_dns_getnetbyname_r (const char *nam + net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); + + anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf, +- 1024, &net_buffer.ptr, NULL, NULL, NULL); ++ 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL); + if (anslen < 0) + { + /* Nothing found. */ +@@ -205,7 +205,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, i + net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); + + anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf, +- 1024, &net_buffer.ptr, NULL, NULL, NULL); ++ 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL); + if (anslen < 0) + { + /* Nothing found. */ +Index: glibc-2.17-c758a686/resolv/res_query.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/res_query.c ++++ glibc-2.17-c758a686/resolv/res_query.c +@@ -98,7 +98,7 @@ static int + __libc_res_nquerydomain(res_state statp, const char *name, const char *domain, + int class, int type, u_char *answer, int anslen, + u_char **answerp, u_char **answerp2, int *nanswerp2, +- int *resplen2); ++ int *resplen2, int *answerp2_malloced); + + /* + * Formulate a normal query, send, and await answer. +@@ -119,7 +119,8 @@ __libc_res_nquery(res_state statp, + u_char **answerp, /* if buffer needs to be enlarged */ + u_char **answerp2, + int *nanswerp2, +- int *resplen2) ++ int *resplen2, ++ int *answerp2_malloced) + { + HEADER *hp = (HEADER *) answer; + HEADER *hp2; +@@ -224,7 +225,8 @@ __libc_res_nquery(res_state statp, + } + assert (answerp == NULL || (void *) *answerp == (void *) answer); + n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer, +- anslen, answerp, answerp2, nanswerp2, resplen2); ++ anslen, answerp, answerp2, nanswerp2, resplen2, ++ answerp2_malloced); + if (use_malloc) + free (buf); + if (n < 0) { +@@ -316,7 +318,7 @@ res_nquery(res_state statp, + int anslen) /* size of answer buffer */ + { + return __libc_res_nquery(statp, name, class, type, answer, anslen, +- NULL, NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL, NULL); + } + libresolv_hidden_def (res_nquery) + +@@ -335,7 +337,8 @@ __libc_res_nsearch(res_state statp, + u_char **answerp, + u_char **answerp2, + int *nanswerp2, +- int *resplen2) ++ int *resplen2, ++ int *answerp2_malloced) + { + const char *cp, * const *domain; + HEADER *hp = (HEADER *) answer; +@@ -360,7 +363,7 @@ __libc_res_nsearch(res_state statp, + if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) + return (__libc_res_nquery(statp, cp, class, type, answer, + anslen, answerp, answerp2, +- nanswerp2, resplen2)); ++ nanswerp2, resplen2, answerp2_malloced)); + + #ifdef DEBUG + if (statp->options & RES_DEBUG) +@@ -377,7 +380,8 @@ __libc_res_nsearch(res_state statp, + if (dots >= statp->ndots || trailing_dot) { + ret = __libc_res_nquerydomain(statp, name, NULL, class, type, + answer, anslen, answerp, +- answerp2, nanswerp2, resplen2); ++ answerp2, nanswerp2, resplen2, ++ answerp2_malloced); + if (ret > 0 || trailing_dot + /* If the second response is valid then we use that. */ + || (ret == 0 && resplen2 != NULL && *resplen2 > 0)) +@@ -388,11 +392,11 @@ __libc_res_nsearch(res_state statp, + answer = *answerp; + anslen = MAXPACKET; + } +- if (answerp2 +- && (*answerp2 < answer || *answerp2 >= answer + anslen)) ++ if (answerp2 && *answerp2_malloced) + { + free (*answerp2); + *answerp2 = NULL; ++ *answerp2_malloced = 0; + } + } + +@@ -419,7 +423,7 @@ __libc_res_nsearch(res_state statp, + class, type, + answer, anslen, answerp, + answerp2, nanswerp2, +- resplen2); ++ resplen2, answerp2_malloced); + if (ret > 0 || (ret == 0 && resplen2 != NULL + && *resplen2 > 0)) + return (ret); +@@ -428,12 +432,11 @@ __libc_res_nsearch(res_state statp, + answer = *answerp; + anslen = MAXPACKET; + } +- if (answerp2 +- && (*answerp2 < answer +- || *answerp2 >= answer + anslen)) ++ if (answerp2 && *answerp2_malloced) + { + free (*answerp2); + *answerp2 = NULL; ++ *answerp2_malloced = 0; + } + + /* +@@ -489,7 +492,8 @@ __libc_res_nsearch(res_state statp, + && !(tried_as_is || root_on_list)) { + ret = __libc_res_nquerydomain(statp, name, NULL, class, type, + answer, anslen, answerp, +- answerp2, nanswerp2, resplen2); ++ answerp2, nanswerp2, resplen2, ++ answerp2_malloced); + if (ret > 0 || (ret == 0 && resplen2 != NULL + && *resplen2 > 0)) + return (ret); +@@ -502,10 +506,11 @@ __libc_res_nsearch(res_state statp, + * else send back meaningless H_ERRNO, that being the one from + * the last DNSRCH we did. + */ +- if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen)) ++ if (answerp2 && *answerp2_malloced) + { + free (*answerp2); + *answerp2 = NULL; ++ *answerp2_malloced = 0; + } + if (saved_herrno != -1) + RES_SET_H_ERRNO(statp, saved_herrno); +@@ -525,7 +530,7 @@ res_nsearch(res_state statp, + int anslen) /* size of answer */ + { + return __libc_res_nsearch(statp, name, class, type, answer, +- anslen, NULL, NULL, NULL, NULL); ++ anslen, NULL, NULL, NULL, NULL, NULL); + } + libresolv_hidden_def (res_nsearch) + +@@ -543,7 +548,8 @@ __libc_res_nquerydomain(res_state statp, + u_char **answerp, + u_char **answerp2, + int *nanswerp2, +- int *resplen2) ++ int *resplen2, ++ int *answerp2_malloced) + { + char nbuf[MAXDNAME]; + const char *longname = nbuf; +@@ -585,7 +591,7 @@ __libc_res_nquerydomain(res_state statp, + } + return (__libc_res_nquery(statp, longname, class, type, answer, + anslen, answerp, answerp2, nanswerp2, +- resplen2)); ++ resplen2, answerp2_malloced)); + } + + int +@@ -597,7 +603,8 @@ res_nquerydomain(res_state statp, + int anslen) /* size of answer */ + { + return __libc_res_nquerydomain(statp, name, domain, class, type, +- answer, anslen, NULL, NULL, NULL, NULL); ++ answer, anslen, NULL, NULL, NULL, NULL, ++ NULL); + } + libresolv_hidden_def (res_nquerydomain) + +Index: glibc-2.17-c758a686/resolv/res_send.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/res_send.c ++++ glibc-2.17-c758a686/resolv/res_send.c +@@ -186,12 +186,12 @@ evNowTime(struct timespec *res) { + static int send_vc(res_state, const u_char *, int, + const u_char *, int, + u_char **, int *, int *, int, u_char **, +- u_char **, int *, int *); ++ u_char **, int *, int *, int *); + static int send_dg(res_state, const u_char *, int, + const u_char *, int, + u_char **, int *, int *, int, + int *, int *, u_char **, +- u_char **, int *, int *); ++ u_char **, int *, int *, int *); + #ifdef DEBUG + static void Aerror(const res_state, FILE *, const char *, int, + const struct sockaddr *); +@@ -343,7 +343,7 @@ int + __libc_res_nsend(res_state statp, const u_char *buf, int buflen, + const u_char *buf2, int buflen2, + u_char *ans, int anssiz, u_char **ansp, u_char **ansp2, +- int *nansp2, int *resplen2) ++ int *nansp2, int *resplen2, int *ansp2_malloced) + { + int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; + +@@ -546,7 +546,8 @@ __libc_res_nsend(res_state statp, const + try = statp->retry; + n = send_vc(statp, buf, buflen, buf2, buflen2, + &ans, &anssiz, &terrno, +- ns, ansp, ansp2, nansp2, resplen2); ++ ns, ansp, ansp2, nansp2, resplen2, ++ ansp2_malloced); + if (n < 0) + return (-1); + if (n == 0 && (buf2 == NULL || *resplen2 == 0)) +@@ -556,7 +557,7 @@ __libc_res_nsend(res_state statp, const + n = send_dg(statp, buf, buflen, buf2, buflen2, + &ans, &anssiz, &terrno, + ns, &v_circuit, &gotsomewhere, ansp, +- ansp2, nansp2, resplen2); ++ ansp2, nansp2, resplen2, ansp2_malloced); + if (n < 0) + return (-1); + if (n == 0 && (buf2 == NULL || *resplen2 == 0)) +@@ -646,7 +647,7 @@ res_nsend(res_state statp, + const u_char *buf, int buflen, u_char *ans, int anssiz) + { + return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz, +- NULL, NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL, NULL); + } + libresolv_hidden_def (res_nsend) + +@@ -657,7 +658,7 @@ send_vc(res_state statp, + const u_char *buf, int buflen, const u_char *buf2, int buflen2, + u_char **ansp, int *anssizp, + int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2, +- int *resplen2) ++ int *resplen2, int *ansp2_malloced) + { + const HEADER *hp = (HEADER *) buf; + const HEADER *hp2 = (HEADER *) buf2; +@@ -823,6 +824,8 @@ send_vc(res_state statp, + } + *thisanssizp = MAXPACKET; + *thisansp = newp; ++ if (thisansp == ansp2) ++ *ansp2_malloced = 1; + anhp = (HEADER *) newp; + len = rlen; + } else { +@@ -992,7 +995,7 @@ send_dg(res_state statp, + const u_char *buf, int buflen, const u_char *buf2, int buflen2, + u_char **ansp, int *anssizp, + int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp, +- u_char **ansp2, int *anssizp2, int *resplen2) ++ u_char **ansp2, int *anssizp2, int *resplen2, int *ansp2_malloced) + { + const HEADER *hp = (HEADER *) buf; + const HEADER *hp2 = (HEADER *) buf2; +@@ -1235,6 +1238,8 @@ send_dg(res_state statp, + if (newp != NULL) { + *anssizp = MAXPACKET; + *thisansp = ans = newp; ++ if (thisansp == ansp2) ++ *ansp2_malloced = 1; + } + } + HEADER *anhp = (HEADER *) *thisansp; diff --git a/SOURCES/glibc-rh1296031-2.patch b/SOURCES/glibc-rh1296031-2.patch new file mode 100644 index 0000000..c46d6bd --- /dev/null +++ b/SOURCES/glibc-rh1296031-2.patch @@ -0,0 +1,38 @@ +commit 6b142b3a1d007d7e6f50c26710de7177bc4aca74 +Author: Andreas Schwab +Date: Mon Jun 8 15:21:18 2015 +0200 + + Record TTL also for DNS PTR queries (bug 18513) + + This allows nscd to manage proper TTL for GETHOSTBYADDR[v6] requests. + +2015-06-22 Andreas Schwab + + [BZ #18513] + * resolv/nss_dns/dns-host.c (getanswer_r): Record TTL also for + PTR queries. + +Index: glibc-2.17-c758a686/resolv/nss_dns/dns-host.c +=================================================================== +--- glibc-2.17-c758a686.orig/resolv/nss_dns/dns-host.c ++++ glibc-2.17-c758a686/resolv/nss_dns/dns-host.c +@@ -800,6 +800,10 @@ getanswer_r (const querybuf *answer, int + + if (qtype == T_PTR && type == T_CNAME) + { ++ /* A CNAME could also have a TTL entry. */ ++ if (ttlp != NULL && ttl < *ttlp) ++ *ttlp = ttl; ++ + n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf); + if (__builtin_expect (n < 0 || res_dnok (tbuf) == 0, 0)) + { +@@ -863,6 +867,8 @@ getanswer_r (const querybuf *answer, int + ++had_error; + break; + } ++ if (ttlp != NULL && ttl < *ttlp) ++ *ttlp = ttl; + #if MULTI_PTRS_ARE_ALIASES + cp += n; + if (haveanswer == 0) diff --git a/SOURCES/glibc-rh1296031.patch b/SOURCES/glibc-rh1296031.patch new file mode 100644 index 0000000..06726e2 --- /dev/null +++ b/SOURCES/glibc-rh1296031.patch @@ -0,0 +1,546 @@ +Index: b/resolv/nss_dns/dns-host.c +=================================================================== +--- a/resolv/nss_dns/dns-host.c ++++ b/resolv/nss_dns/dns-host.c +@@ -1051,7 +1051,10 @@ gaih_getanswer_slice (const querybuf *an + int h_namelen = 0; + + if (ancount == 0) +- return NSS_STATUS_NOTFOUND; ++ { ++ *h_errnop = HOST_NOT_FOUND; ++ return NSS_STATUS_NOTFOUND; ++ } + + while (ancount-- > 0 && cp < end_of_message && had_error == 0) + { +@@ -1228,7 +1231,14 @@ gaih_getanswer_slice (const querybuf *an + /* Special case here: if the resolver sent a result but it only + contains a CNAME while we are looking for a T_A or T_AAAA record, + we fail with NOTFOUND instead of TRYAGAIN. */ +- return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; ++ if (canon != NULL) ++ { ++ *h_errnop = HOST_NOT_FOUND; ++ return NSS_STATUS_NOTFOUND; ++ } ++ ++ *h_errnop = NETDB_INTERNAL; ++ return NSS_STATUS_TRYAGAIN; + } + + +@@ -1242,11 +1252,101 @@ gaih_getanswer (const querybuf *answer1, + + enum nss_status status = NSS_STATUS_NOTFOUND; + ++ /* Combining the NSS status of two distinct queries requires some ++ compromise and attention to symmetry (A or AAAA queries can be ++ returned in any order). What follows is a breakdown of how this ++ code is expected to work and why. We discuss only SUCCESS, ++ TRYAGAIN, NOTFOUND and UNAVAIL, since they are the only returns ++ that apply (though RETURN and MERGE exist). We make a distinction ++ between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable). ++ A recoverable TRYAGAIN is almost always due to buffer size issues ++ and returns ERANGE in errno and the caller is expected to retry ++ with a larger buffer. ++ ++ Lastly, you may be tempted to make significant changes to the ++ conditions in this code to bring about symmetry between responses. ++ Please don't change anything without due consideration for ++ expected application behaviour. Some of the synthesized responses ++ aren't very well thought out and sometimes appear to imply that ++ IPv4 responses are always answer 1, and IPv6 responses are always ++ answer 2, but that's not true (see the implemetnation of send_dg ++ and send_vc to see response can arrive in any order, particlarly ++ for UDP). However, we expect it holds roughly enough of the time ++ that this code works, but certainly needs to be fixed to make this ++ a more robust implementation. ++ ++ ---------------------------------------------- ++ | Answer 1 Status / | Synthesized | Reason | ++ | Answer 2 Status | Status | | ++ |--------------------------------------------| ++ | SUCCESS/SUCCESS | SUCCESS | [1] | ++ | SUCCESS/TRYAGAIN | TRYAGAIN | [5] | ++ | SUCCESS/TRYAGAIN' | SUCCESS | [1] | ++ | SUCCESS/NOTFOUND | SUCCESS | [1] | ++ | SUCCESS/UNAVAIL | SUCCESS | [1] | ++ | TRYAGAIN/SUCCESS | TRYAGAIN | [2] | ++ | TRYAGAIN/TRYAGAIN | TRYAGAIN | [2] | ++ | TRYAGAIN/TRYAGAIN' | TRYAGAIN | [2] | ++ | TRYAGAIN/NOTFOUND | TRYAGAIN | [2] | ++ | TRYAGAIN/UNAVAIL | TRYAGAIN | [2] | ++ | TRYAGAIN'/SUCCESS | SUCCESS | [3] | ++ | TRYAGAIN'/TRYAGAIN | TRYAGAIN | [3] | ++ | TRYAGAIN'/TRYAGAIN' | TRYAGAIN' | [3] | ++ | TRYAGAIN'/NOTFOUND | TRYAGAIN' | [3] | ++ | TRYAGAIN'/UNAVAIL | UNAVAIL | [3] | ++ | NOTFOUND/SUCCESS | SUCCESS | [3] | ++ | NOTFOUND/TRYAGAIN | TRYAGAIN | [3] | ++ | NOTFOUND/TRYAGAIN' | TRYAGAIN' | [3] | ++ | NOTFOUND/NOTFOUND | NOTFOUND | [3] | ++ | NOTFOUND/UNAVAIL | UNAVAIL | [3] | ++ | UNAVAIL/SUCCESS | UNAVAIL | [4] | ++ | UNAVAIL/TRYAGAIN | UNAVAIL | [4] | ++ | UNAVAIL/TRYAGAIN' | UNAVAIL | [4] | ++ | UNAVAIL/NOTFOUND | UNAVAIL | [4] | ++ | UNAVAIL/UNAVAIL | UNAVAIL | [4] | ++ ---------------------------------------------- ++ ++ [1] If the first response is a success we return success. ++ This ignores the state of the second answer and in fact ++ incorrectly sets errno and h_errno to that of the second ++ answer. However because the response is a success we ignore ++ *errnop and *h_errnop (though that means you touched errno on ++ success). We are being conservative here and returning the ++ likely IPv4 response in the first answer as a success. ++ ++ [2] If the first response is a recoverable TRYAGAIN we return ++ that instead of looking at the second response. The ++ expectation here is that we have failed to get an IPv4 response ++ and should retry both queries. ++ ++ [3] If the first response was not a SUCCESS and the second ++ response is not NOTFOUND (had a SUCCESS, need to TRYAGAIN, ++ or failed entirely e.g. TRYAGAIN' and UNAVAIL) then use the ++ result from the second response, otherwise the first responses ++ status is used. Again we have some odd side-effects when the ++ second response is NOTFOUND because we overwrite *errnop and ++ *h_errnop that means that a first answer of NOTFOUND might see ++ its *errnop and *h_errnop values altered. Whether it matters ++ in practice that a first response NOTFOUND has the wrong ++ *errnop and *h_errnop is undecided. ++ ++ [4] If the first response is UNAVAIL we return that instead of ++ looking at the second response. The expectation here is that ++ it will have failed similarly e.g. configuration failure. ++ ++ [5] Testing this code is complicated by the fact that truncated ++ second response buffers might be returned as SUCCESS if the ++ first answer is a SUCCESS. To fix this we add symmetry to ++ TRYAGAIN with the second response. If the second response ++ is a recoverable error we now return TRYAGIN even if the first ++ response was SUCCESS. */ ++ + if (anslen1 > 0) + status = gaih_getanswer_slice(answer1, anslen1, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, + &first); ++ + if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND + || (status == NSS_STATUS_TRYAGAIN + /* We want to look at the second answer in case of an +@@ -1262,8 +1362,15 @@ gaih_getanswer (const querybuf *answer1, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, + &first); ++ /* Use the second response status in some cases. */ + if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND) + status = status2; ++ /* Do not return a truncated second response (unless it was ++ unavoidable e.g. unrecoverable TRYAGAIN). */ ++ if (status == NSS_STATUS_SUCCESS ++ && (status2 == NSS_STATUS_TRYAGAIN ++ && *errnop == ERANGE && *h_errnop != NO_RECOVERY)) ++ status = NSS_STATUS_TRYAGAIN; + } + + return status; +Index: b/resolv/res_query.c +=================================================================== +--- a/resolv/res_query.c ++++ b/resolv/res_query.c +@@ -396,6 +396,7 @@ __libc_res_nsearch(res_state statp, + { + free (*answerp2); + *answerp2 = NULL; ++ *nanswerp2 = 0; + *answerp2_malloced = 0; + } + } +@@ -436,6 +437,7 @@ __libc_res_nsearch(res_state statp, + { + free (*answerp2); + *answerp2 = NULL; ++ *nanswerp2 = 0; + *answerp2_malloced = 0; + } + +@@ -510,6 +512,7 @@ __libc_res_nsearch(res_state statp, + { + free (*answerp2); + *answerp2 = NULL; ++ *nanswerp2 = 0; + *answerp2_malloced = 0; + } + if (saved_herrno != -1) +Index: b/resolv/res_send.c +=================================================================== +--- a/resolv/res_send.c ++++ b/resolv/res_send.c +@@ -1,3 +1,20 @@ ++/* Copyright (C) 2016 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ + /* + * Copyright (c) 1985, 1989, 1993 + * The Regents of the University of California. All rights reserved. +@@ -360,6 +377,8 @@ __libc_res_nsend(res_state statp, const + #ifdef USE_HOOKS + if (__builtin_expect (statp->qhook || statp->rhook, 0)) { + if (anssiz < MAXPACKET && ansp) { ++ /* Always allocate MAXPACKET, callers expect ++ this specific size. */ + u_char *buf = malloc (MAXPACKET); + if (buf == NULL) + return (-1); +@@ -653,6 +672,77 @@ libresolv_hidden_def (res_nsend) + + /* Private */ + ++/* The send_vc function is responsible for sending a DNS query over TCP ++ to the nameserver numbered NS from the res_state STATP i.e. ++ EXT(statp).nssocks[ns]. The function supports sending both IPv4 and ++ IPv6 queries at the same serially on the same socket. ++ ++ Please note that for TCP there is no way to disable sending both ++ queries, unlike UDP, which honours RES_SNGLKUP and RES_SNGLKUPREOP ++ and sends the queries serially and waits for the result after each ++ sent query. This implemetnation should be corrected to honour these ++ options. ++ ++ Please also note that for TCP we send both queries over the same ++ socket one after another. This technically violates best practice ++ since the server is allowed to read the first query, respond, and ++ then close the socket (to service another client). If the server ++ does this, then the remaining second query in the socket data buffer ++ will cause the server to send the client an RST which will arrive ++ asynchronously and the client's OS will likely tear down the socket ++ receive buffer resulting in a potentially short read and lost ++ response data. This will force the client to retry the query again, ++ and this process may repeat until all servers and connection resets ++ are exhausted and then the query will fail. It's not known if this ++ happens with any frequency in real DNS server implementations. This ++ implementation should be corrected to use two sockets by default for ++ parallel queries. ++ ++ The query stored in BUF of BUFLEN length is sent first followed by ++ the query stored in BUF2 of BUFLEN2 length. Queries are sent ++ serially on the same socket. ++ ++ Answers to the query are stored firstly in *ANSP up to a max of ++ *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP ++ is non-NULL (to indicate that modifying the answer buffer is allowed) ++ then malloc is used to allocate a new response buffer and ANSCP and ++ ANSP will both point to the new buffer. If more than *ANSSIZP bytes ++ are needed but ANSCP is NULL, then as much of the response as ++ possible is read into the buffer, but the results will be truncated. ++ When truncation happens because of a small answer buffer the DNS ++ packets header feild TC will bet set to 1, indicating a truncated ++ message and the rest of the socket data will be read and discarded. ++ ++ Answers to the query are stored secondly in *ANSP2 up to a max of ++ *ANSSIZP2 bytes, with the actual response length stored in ++ *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2 ++ is non-NULL (required for a second query) then malloc is used to ++ allocate a new response buffer, *ANSSIZP2 is set to the new buffer ++ size and *ANSP2_MALLOCED is set to 1. ++ ++ The ANSP2_MALLOCED argument will eventually be removed as the ++ change in buffer pointer can be used to detect the buffer has ++ changed and that the caller should use free on the new buffer. ++ ++ Note that the answers may arrive in any order from the server and ++ therefore the first and second answer buffers may not correspond to ++ the first and second queries. ++ ++ It is not supported to call this function with a non-NULL ANSP2 ++ but a NULL ANSCP. Put another way, you can call send_vc with a ++ single unmodifiable buffer or two modifiable buffers, but no other ++ combination is supported. ++ ++ It is the caller's responsibility to free the malloc allocated ++ buffers by detecting that the pointers have changed from their ++ original values i.e. *ANSCP or *ANSP2 has changed. ++ ++ If errors are encountered then *TERRNO is set to an appropriate ++ errno value and a zero result is returned for a recoverable error, ++ and a less-than zero result is returned for a non-recoverable error. ++ ++ If no errors are encountered then *TERRNO is left unmodified and ++ a the length of the first response in bytes is returned. */ + static int + send_vc(res_state statp, + const u_char *buf, int buflen, const u_char *buf2, int buflen2, +@@ -662,11 +752,7 @@ send_vc(res_state statp, + { + const HEADER *hp = (HEADER *) buf; + const HEADER *hp2 = (HEADER *) buf2; +- u_char *ans = *ansp; +- int orig_anssizp = *anssizp; +- // XXX REMOVE +- // int anssiz = *anssizp; +- HEADER *anhp = (HEADER *) ans; ++ HEADER *anhp = (HEADER *) *ansp; + struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; + int truncating, connreset, resplen, n; + struct iovec iov[4]; +@@ -742,6 +828,8 @@ send_vc(res_state statp, + * Receive length & response + */ + int recvresp1 = 0; ++ /* Skip the second response if there is no second query. ++ To do that we mark the second response as received. */ + int recvresp2 = buf2 == NULL; + uint16_t rlen16; + read_len: +@@ -778,33 +866,14 @@ send_vc(res_state statp, + u_char **thisansp; + int *thisresplenp; + if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) { ++ /* We have not received any responses ++ yet or we only have one response to ++ receive. */ + thisanssizp = anssizp; + thisansp = anscp ?: ansp; + assert (anscp != NULL || ansp2 == NULL); + thisresplenp = &resplen; + } else { +- if (*anssizp != MAXPACKET) { +- /* No buffer allocated for the first +- reply. We can try to use the rest +- of the user-provided buffer. */ +-#ifdef _STRING_ARCH_unaligned +- *anssizp2 = orig_anssizp - resplen; +- *ansp2 = *ansp + resplen; +-#else +- int aligned_resplen +- = ((resplen + __alignof__ (HEADER) - 1) +- & ~(__alignof__ (HEADER) - 1)); +- *anssizp2 = orig_anssizp - aligned_resplen; +- *ansp2 = *ansp + aligned_resplen; +-#endif +- } else { +- /* The first reply did not fit into the +- user-provided buffer. Maybe the second +- answer will. */ +- *anssizp2 = orig_anssizp; +- *ansp2 = *ansp; +- } +- + thisanssizp = anssizp2; + thisansp = ansp2; + thisresplenp = resplen2; +@@ -812,10 +881,14 @@ send_vc(res_state statp, + anhp = (HEADER *) *thisansp; + + *thisresplenp = rlen; +- if (rlen > *thisanssizp) { +- /* Yes, we test ANSCP here. If we have two buffers +- both will be allocatable. */ +- if (__builtin_expect (anscp != NULL, 1)) { ++ /* Is the answer buffer too small? */ ++ if (*thisanssizp < rlen) { ++ /* If the current buffer is non-NULL and it's not ++ pointing at the static user-supplied buffer then ++ we can reallocate it. */ ++ if (thisansp != NULL && thisansp != ansp) { ++ /* Always allocate MAXPACKET, callers expect ++ this specific size. */ + u_char *newp = malloc (MAXPACKET); + if (newp == NULL) { + *terrno = ENOMEM; +@@ -827,6 +900,9 @@ send_vc(res_state statp, + if (thisansp == ansp2) + *ansp2_malloced = 1; + anhp = (HEADER *) newp; ++ /* A uint16_t can't be larger than MAXPACKET ++ thus it's safe to allocate MAXPACKET but ++ read RLEN bytes instead. */ + len = rlen; + } else { + Dprint(statp->options & RES_DEBUG, +@@ -990,6 +1066,66 @@ reopen (res_state statp, int *terrno, in + return 1; + } + ++/* The send_dg function is responsible for sending a DNS query over UDP ++ to the nameserver numbered NS from the res_state STATP i.e. ++ EXT(statp).nssocks[ns]. The function supports IPv4 and IPv6 queries ++ along with the ability to send the query in parallel for both stacks ++ (default) or serially (RES_SINGLKUP). It also supports serial lookup ++ with a close and reopen of the socket used to talk to the server ++ (RES_SNGLKUPREOP) to work around broken name servers. ++ ++ The query stored in BUF of BUFLEN length is sent first followed by ++ the query stored in BUF2 of BUFLEN2 length. Queries are sent ++ in parallel (default) or serially (RES_SINGLKUP or RES_SNGLKUPREOP). ++ ++ Answers to the query are stored firstly in *ANSP up to a max of ++ *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP ++ is non-NULL (to indicate that modifying the answer buffer is allowed) ++ then malloc is used to allocate a new response buffer and ANSCP and ++ ANSP will both point to the new buffer. If more than *ANSSIZP bytes ++ are needed but ANSCP is NULL, then as much of the response as ++ possible is read into the buffer, but the results will be truncated. ++ When truncation happens because of a small answer buffer the DNS ++ packets header feild TC will bet set to 1, indicating a truncated ++ message, while the rest of the UDP packet is discarded. ++ ++ Answers to the query are stored secondly in *ANSP2 up to a max of ++ *ANSSIZP2 bytes, with the actual response length stored in ++ *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2 ++ is non-NULL (required for a second query) then malloc is used to ++ allocate a new response buffer, *ANSSIZP2 is set to the new buffer ++ size and *ANSP2_MALLOCED is set to 1. ++ ++ The ANSP2_MALLOCED argument will eventually be removed as the ++ change in buffer pointer can be used to detect the buffer has ++ changed and that the caller should use free on the new buffer. ++ ++ Note that the answers may arrive in any order from the server and ++ therefore the first and second answer buffers may not correspond to ++ the first and second queries. ++ ++ It is not supported to call this function with a non-NULL ANSP2 ++ but a NULL ANSCP. Put another way, you can call send_vc with a ++ single unmodifiable buffer or two modifiable buffers, but no other ++ combination is supported. ++ ++ It is the caller's responsibility to free the malloc allocated ++ buffers by detecting that the pointers have changed from their ++ original values i.e. *ANSCP or *ANSP2 has changed. ++ ++ If an answer is truncated because of UDP datagram DNS limits then ++ *V_CIRCUIT is set to 1 and the return value non-zero to indicate to ++ the caller to retry with TCP. The value *GOTSOMEWHERE is set to 1 ++ if any progress was made reading a response from the nameserver and ++ is used by the caller to distinguish between ECONNREFUSED and ++ ETIMEDOUT (the latter if *GOTSOMEWHERE is 1). ++ ++ If errors are encountered then *TERRNO is set to an appropriate ++ errno value and a zero result is returned for a recoverable error, ++ and a less-than zero result is returned for a non-recoverable error. ++ ++ If no errors are encountered then *TERRNO is left unmodified and ++ a the length of the first response in bytes is returned. */ + static int + send_dg(res_state statp, + const u_char *buf, int buflen, const u_char *buf2, int buflen2, +@@ -999,8 +1135,6 @@ send_dg(res_state statp, + { + const HEADER *hp = (HEADER *) buf; + const HEADER *hp2 = (HEADER *) buf2; +- u_char *ans = *ansp; +- int orig_anssizp = *anssizp; + struct timespec now, timeout, finish; + struct pollfd pfd[1]; + int ptimeout; +@@ -1033,6 +1167,8 @@ send_dg(res_state statp, + int need_recompute = 0; + int nwritten = 0; + int recvresp1 = 0; ++ /* Skip the second response if there is no second query. ++ To do that we mark the second response as received. */ + int recvresp2 = buf2 == NULL; + pfd[0].fd = EXT(statp).nssocks[ns]; + pfd[0].events = POLLOUT; +@@ -1196,52 +1332,54 @@ send_dg(res_state statp, + int *thisresplenp; + + if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) { ++ /* We have not received any responses ++ yet or we only have one response to ++ receive. */ + thisanssizp = anssizp; + thisansp = anscp ?: ansp; + assert (anscp != NULL || ansp2 == NULL); + thisresplenp = &resplen; + } else { +- if (*anssizp != MAXPACKET) { +- /* No buffer allocated for the first +- reply. We can try to use the rest +- of the user-provided buffer. */ +-#ifdef _STRING_ARCH_unaligned +- *anssizp2 = orig_anssizp - resplen; +- *ansp2 = *ansp + resplen; +-#else +- int aligned_resplen +- = ((resplen + __alignof__ (HEADER) - 1) +- & ~(__alignof__ (HEADER) - 1)); +- *anssizp2 = orig_anssizp - aligned_resplen; +- *ansp2 = *ansp + aligned_resplen; +-#endif +- } else { +- /* The first reply did not fit into the +- user-provided buffer. Maybe the second +- answer will. */ +- *anssizp2 = orig_anssizp; +- *ansp2 = *ansp; +- } +- + thisanssizp = anssizp2; + thisansp = ansp2; + thisresplenp = resplen2; + } + + if (*thisanssizp < MAXPACKET +- /* Yes, we test ANSCP here. If we have two buffers +- both will be allocatable. */ +- && anscp ++ /* If the current buffer is non-NULL and it's not ++ pointing at the static user-supplied buffer then ++ we can reallocate it. */ ++ && (thisansp != NULL && thisansp != ansp) ++ /* Is the size too small? */ + && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0 +- || *thisanssizp < *thisresplenp)) { ++ || *thisanssizp < *thisresplenp) ++ ) { ++ /* Always allocate MAXPACKET, callers expect ++ this specific size. */ + u_char *newp = malloc (MAXPACKET); + if (newp != NULL) { +- *anssizp = MAXPACKET; +- *thisansp = ans = newp; ++ *thisanssizp = MAXPACKET; ++ *thisansp = newp; + if (thisansp == ansp2) + *ansp2_malloced = 1; + } + } ++ /* We could end up with truncation if anscp was NULL ++ (not allowed to change caller's buffer) and the ++ response buffer size is too small. This isn't a ++ reliable way to detect truncation because the ioctl ++ may be an inaccurate report of the UDP message size. ++ Therefore we use this only to issue debug output. ++ To do truncation accurately with UDP we need ++ MSG_TRUNC which is only available on Linux. We ++ can abstract out the Linux-specific feature in the ++ future to detect truncation. */ ++ if (__glibc_unlikely (*thisanssizp < *thisresplenp)) { ++ Dprint(statp->options & RES_DEBUG, ++ (stdout, ";; response may be truncated (UDP)\n") ++ ); ++ } ++ + HEADER *anhp = (HEADER *) *thisansp; + socklen_t fromlen = sizeof(struct sockaddr_in6); + assert (sizeof(from) <= fromlen); diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index 1aa0682..4f2ec5b 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -1,6 +1,6 @@ %define glibcsrcdir glibc-2.17-c758a686 %define glibcversion 2.17 -%define glibcrelease 105%{?dist} +%define glibcrelease 106%{?dist}.4 ############################################################################## # If run_glibc_tests is zero then tests are not run for the build. # You must always set run_glibc_tests to one for production builds. @@ -88,8 +88,8 @@ URL: http://www.gnu.org/software/glibc/ # We do not use usptream source tarballs as the start place for our package. # We should use upstream source tarballs for official releases though and # it will look like this: -# Source0: http://ftp.gnu.org/gnu/glibc/%{glibcsrcdir}.tar.gz -# Source1: %{glibcsrcdir}-releng.tar.gz +# Source0: http://ftp.gnu.org/gnu/glibc/%%{glibcsrcdir}.tar.gz +# Source1: %%{glibcsrcdir}-releng.tar.gz # TODO: # The Source1 URL will never reference an upstream URL. In fact the plan # should be to merge the entire release engineering tarball into upstream @@ -201,6 +201,9 @@ Patch0063: glibc-rh1120490-int128.patch # Workaround to extend DTV_SURPLUS. Not to go upstream. Patch0066: glibc-rh1227699.patch +# CVE-2015-7547 +Patch0067: glibc-rh1296031.patch + ############################################################################## # # Patches from upstream @@ -653,6 +656,23 @@ Patch1610: glibc-rh1234622.patch # Fix 32-bit POWER assembly to use only 32-bit instructions. Patch1611: glibc-rh1240796.patch +# CVE-2015-5229 and regression test. +Patch1612: glibc-rh1293976.patch +Patch1613: glibc-rh1293976-2.patch + +# BZ #16574 +Patch1614: glibc-rh1296031-0.patch +# BZ #13928 +Patch1616: glibc-rh1296031-2.patch + +# Malloc trim fixes: #17195, #18502. +Patch1617: glibc-rh1284959-1.patch +Patch1618: glibc-rh1284959-2.patch +Patch1619: glibc-rh1284959-3.patch + +# ppc64le monstartup fix: +Patch1620: glibc-rh1249102.patch + ############################################################################## # # Patches submitted, but not yet approved upstream. @@ -1291,6 +1311,15 @@ package or when debugging this package. %patch1611 -p1 %patch1123 -p1 %patch1124 -p1 +%patch1612 -p1 +%patch1613 -p1 +%patch1614 -p1 +%patch1616 -p1 +%patch0067 -p1 +%patch1617 -p1 +%patch1618 -p1 +%patch1619 -p1 +%patch1620 -p1 ############################################################################## # %%prep - Additional prep required... @@ -2360,6 +2389,24 @@ rm -f *.filelist* %endif %changelog +* Fri Feb 5 2016 Florian Weimer - 2.17-106.4 +- Revert problematic libresolv change, not needed for the + CVE-2015-7547 fix (#1296030). + +* Fri Jan 15 2016 Carlos O'Donell - 2.17-106.3 +- Fix CVE-2015-7547: getaddrinfo() stack-based buffer overflow (#1296030). +- Fix madvise performance issues (#1298930). +- Avoid "monstartup: out of memory" error on powerpc64le (#1298956). + +* Wed Jan 13 2016 Carlos O'Donell - 2.17-106.2 +- Fix CVE-2015-5229: calloc() may return non-zero memory (#1296453). + +* Wed Oct 28 2015 Florian Weimer - 2.17-106.1 +- Rebuild with corrected release. + +* Wed Oct 28 2015 Florian Weimer - 2.17-106 +- Add fix for CVE-2015-5277 (#1275920). + * Fri Aug 14 2015 Siddhesh Poyarekar - 2.17-105 - Fix up test case for initial-exec fix (#1248208).