1 /* Test and verify that too-large memory allocations fail with ENOMEM.
2    Copyright (C) 2018-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* Bug 22375 reported a regression in malloc where if after malloc'ing then
20    free'ing a small block of memory, malloc is then called with a really
21    large size argument (close to SIZE_MAX): instead of returning NULL and
22    setting errno to ENOMEM, malloc incorrectly returns the previously
23    allocated block instead.  Bug 22343 reported a similar case where
24    posix_memalign incorrectly returns successfully when called with an with
25    a really large size argument.
26 
27    Both of these were caused by integer overflows in the allocator when it
28    was trying to pad the requested size to allow for book-keeping or
29    alignment.  This test guards against such bugs by repeatedly allocating
30    and freeing small blocks of memory then trying to allocate various block
31    sizes larger than the memory bus width of 64-bit targets, or almost
32    as large as SIZE_MAX on 32-bit targets supported by glibc.  In each case,
33    it verifies that such impossibly large allocations correctly fail.  */
34 
35 
36 #include <stdlib.h>
37 #include <malloc.h>
38 #include <errno.h>
39 #include <stdint.h>
40 #include <sys/resource.h>
41 #include <libc-diag.h>
42 #include <support/check.h>
43 #include <unistd.h>
44 #include <sys/param.h>
45 
46 
47 /* This function prepares for each 'too-large memory allocation' test by
48    performing a small successful malloc/free and resetting errno prior to
49    the actual test.  */
50 static void
test_setup(void)51 test_setup (void)
52 {
53   void *volatile ptr = malloc (16);
54   TEST_VERIFY_EXIT (ptr != NULL);
55   free (ptr);
56   errno = 0;
57 }
58 
59 
60 /* This function tests each of:
61    - malloc (SIZE)
62    - realloc (PTR_FOR_REALLOC, SIZE)
63    - for various values of NMEMB:
64     - calloc (NMEMB, SIZE/NMEMB)
65     - calloc (SIZE/NMEMB, NMEMB)
66     - reallocarray (PTR_FOR_REALLOC, NMEMB, SIZE/NMEMB)
67     - reallocarray (PTR_FOR_REALLOC, SIZE/NMEMB, NMEMB)
68    and precedes each of these tests with a small malloc/free before it.  */
69 static void
test_large_allocations(size_t size)70 test_large_allocations (size_t size)
71 {
72   void * ptr_to_realloc;
73 
74   test_setup ();
75   DIAG_PUSH_NEEDS_COMMENT;
76 #if __GNUC_PREREQ (7, 0)
77   /* GCC 7 warns about too-large allocations; here we want to test
78      that they fail.  */
79   DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
80 #endif
81   TEST_VERIFY (malloc (size) == NULL);
82 #if __GNUC_PREREQ (7, 0)
83   DIAG_POP_NEEDS_COMMENT;
84 #endif
85   TEST_VERIFY (errno == ENOMEM);
86 
87   ptr_to_realloc = malloc (16);
88   TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
89   test_setup ();
90 #if __GNUC_PREREQ (7, 0)
91   DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
92 #endif
93   TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
94 #if __GNUC_PREREQ (7, 0)
95   DIAG_POP_NEEDS_COMMENT;
96 #endif
97   TEST_VERIFY (errno == ENOMEM);
98   free (ptr_to_realloc);
99 
100   for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
101     if ((size % nmemb) == 0)
102       {
103         test_setup ();
104         TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
105         TEST_VERIFY (errno == ENOMEM);
106 
107         test_setup ();
108         TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
109         TEST_VERIFY (errno == ENOMEM);
110 
111         ptr_to_realloc = malloc (16);
112         TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
113         test_setup ();
114         TEST_VERIFY (reallocarray (ptr_to_realloc, nmemb, size / nmemb) == NULL);
115         TEST_VERIFY (errno == ENOMEM);
116         free (ptr_to_realloc);
117 
118         ptr_to_realloc = malloc (16);
119         TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
120         test_setup ();
121         TEST_VERIFY (reallocarray (ptr_to_realloc, size / nmemb, nmemb) == NULL);
122         TEST_VERIFY (errno == ENOMEM);
123         free (ptr_to_realloc);
124       }
125     else
126       break;
127 }
128 
129 
130 static long pagesize;
131 
132 /* This function tests the following aligned memory allocation functions
133    using several valid alignments and precedes each allocation test with a
134    small malloc/free before it:
135    memalign, posix_memalign, aligned_alloc, valloc, pvalloc.  */
136 static void
test_large_aligned_allocations(size_t size)137 test_large_aligned_allocations (size_t size)
138 {
139   /* ptr stores the result of posix_memalign but since all those calls
140      should fail, posix_memalign should never change ptr.  We set it to
141      NULL here and later on we check that it remains NULL after each
142      posix_memalign call.  */
143   void * ptr = NULL;
144 
145   size_t align;
146 
147   /* All aligned memory allocation functions expect an alignment that is a
148      power of 2.  Given this, we test each of them with every valid
149      alignment from 1 thru PAGESIZE.  */
150   for (align = 1; align <= pagesize; align *= 2)
151     {
152       test_setup ();
153 #if __GNUC_PREREQ (7, 0)
154       DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
155 #endif
156       TEST_VERIFY (memalign (align, size) == NULL);
157 #if __GNUC_PREREQ (7, 0)
158       DIAG_POP_NEEDS_COMMENT;
159 #endif
160       TEST_VERIFY (errno == ENOMEM);
161 
162       /* posix_memalign expects an alignment that is a power of 2 *and* a
163          multiple of sizeof (void *).  */
164       if ((align % sizeof (void *)) == 0)
165         {
166           test_setup ();
167           TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
168           TEST_VERIFY (ptr == NULL);
169         }
170 
171       /* aligned_alloc expects a size that is a multiple of alignment.  */
172       if ((size % align) == 0)
173         {
174           test_setup ();
175 #if __GNUC_PREREQ (7, 0)
176 	  DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
177 #endif
178           TEST_VERIFY (aligned_alloc (align, size) == NULL);
179 #if __GNUC_PREREQ (7, 0)
180 	  DIAG_POP_NEEDS_COMMENT;
181 #endif
182           TEST_VERIFY (errno == ENOMEM);
183         }
184     }
185 
186   /* Both valloc and pvalloc return page-aligned memory.  */
187 
188   test_setup ();
189 #if __GNUC_PREREQ (7, 0)
190   DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
191 #endif
192   TEST_VERIFY (valloc (size) == NULL);
193 #if __GNUC_PREREQ (7, 0)
194   DIAG_POP_NEEDS_COMMENT;
195 #endif
196   TEST_VERIFY (errno == ENOMEM);
197 
198   test_setup ();
199 #if __GNUC_PREREQ (7, 0)
200   DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
201 #endif
202   TEST_VERIFY (pvalloc (size) == NULL);
203 #if __GNUC_PREREQ (7, 0)
204   DIAG_POP_NEEDS_COMMENT;
205 #endif
206   TEST_VERIFY (errno == ENOMEM);
207 }
208 
209 
210 #define FOURTEEN_ON_BITS ((1UL << 14) - 1)
211 #define FIFTY_ON_BITS ((1UL << 50) - 1)
212 
213 
214 static int
do_test(void)215 do_test (void)
216 {
217 
218 #if __WORDSIZE >= 64
219 
220   /* This test assumes that none of the supported targets have an address
221      bus wider than 50 bits, and that therefore allocations for sizes wider
222      than 50 bits will fail.  Here, we ensure that the assumption continues
223      to be true in the future when we might have address buses wider than 50
224      bits.  */
225 
226   struct rlimit alloc_size_limit
227     = {
228         .rlim_cur = FIFTY_ON_BITS,
229         .rlim_max = FIFTY_ON_BITS
230       };
231 
232   setrlimit (RLIMIT_AS, &alloc_size_limit);
233 
234 #endif /* __WORDSIZE >= 64 */
235 
236   DIAG_PUSH_NEEDS_COMMENT;
237 #if __GNUC_PREREQ (7, 0)
238   /* GCC 7 warns about too-large allocations; here we want to test
239      that they fail.  */
240   DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
241 #endif
242 
243   /* Aligned memory allocation functions need to be tested up to alignment
244      size equivalent to page size, which should be a power of 2.  */
245   pagesize = sysconf (_SC_PAGESIZE);
246   TEST_VERIFY_EXIT (powerof2 (pagesize));
247 
248   /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
249      in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
250 
251      We can expect that this range of allocation sizes will always lead to
252      an allocation failure on both 64 and 32 bit targets, because:
253 
254      1. no currently supported 64-bit target has an address bus wider than
255      50 bits -- and (2^64 - 2^14) is much wider than that;
256 
257      2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
258      addressable, glibc itself is more than 2^14 bytes in size, and
259      therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
260      available.  */
261 
262   for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
263     {
264       test_large_allocations (SIZE_MAX - i);
265       test_large_aligned_allocations (SIZE_MAX - i);
266     }
267 
268   /* Allocation larger than PTRDIFF_MAX does play well with C standard,
269      since pointer subtraction within the object might overflow ptrdiff_t
270      resulting in undefined behavior.  To prevent it malloc function fail
271      for such allocations.  */
272   for (size_t i = 1; i <= FOURTEEN_ON_BITS; i++)
273     {
274       test_large_allocations (PTRDIFF_MAX + i);
275       test_large_aligned_allocations (PTRDIFF_MAX + i);
276     }
277 
278 #if __WORDSIZE >= 64
279   /* On 64-bit targets, we need to test a much wider range of too-large
280      sizes, so we test at intervals of (1 << 50) that allocation sizes
281      ranging from SIZE_MAX down to (1 << 50) fail:
282      The 14 MSBs are decremented starting from "all ON" going down to 1,
283      the 50 LSBs are "all ON" and then "all OFF" during every iteration.  */
284   for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
285     {
286       size_t size = (msbs << 50) | FIFTY_ON_BITS;
287       test_large_allocations (size);
288       test_large_aligned_allocations (size);
289 
290       size = msbs << 50;
291       test_large_allocations (size);
292       test_large_aligned_allocations (size);
293     }
294 #endif /* __WORDSIZE >= 64 */
295 
296   DIAG_POP_NEEDS_COMMENT;
297 
298   return 0;
299 }
300 
301 
302 #include <support/test-driver.c>
303