1 /*
2  * Copyright (c) 2004, Bull S.A..  All rights reserved.
3  * Created by: Sebastien Decugis
4 
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  *
17 
18  * This sample test aims to check the following assertion:
19  * The function fails and returns ENOMEM if there is not enough memory.
20 
21  * The steps are:
22  * * Fork
23  * * New process sets its memory resource limit to a minimum value, then
24  *  -> Allocate all the available memory
25  *  -> call pthread_mutex_init()
26  *  -> free the memory
27  *  -> Checks that pthread_mutex_init() returned 0 or ENOMEM.
28  * * Parent process waits for the child.
29  */
30 
31 /********************************************************************************************/
32 /****************************** standard includes *****************************************/
33 /********************************************************************************************/
34 #include <pthread.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <sys/wait.h>
41 #include <sys/resource.h>
42 #include <stdarg.h>
43 
44 /********************************************************************************************/
45 /******************************   Test framework   *****************************************/
46 /********************************************************************************************/
47 #include "../testfrmw/testfrmw.h"
48 #include "../testfrmw/testfrmw.c"
49  /* This header is responsible for defining the following macros:
50   * UNRESOLVED(ret, descr);
51   *    where descr is a description of the error and ret is an int (error code for example)
52   * FAILED(descr);
53   *    where descr is a short text saying why the test has failed.
54   * PASSED();
55   *    No parameter.
56   *
57   * Both three macros shall terminate the calling process.
58   * The testcase shall not terminate in any other maneer.
59   *
60   * The other file defines the functions
61   * void output_init()
62   * void output(char * string, ...)
63   *
64   * Those may be used to output information.
65   */
66 
67 /********************************************************************************************/
68 /********************************** Configuration ******************************************/
69 /********************************************************************************************/
70 #ifndef VERBOSE
71 #define VERBOSE 1
72 #endif
73 
74 /********************************************************************************************/
75 /***********************************    Test case   *****************************************/
76 /********************************************************************************************/
77 
main(void)78 int main(void)
79 {
80 	pid_t child;
81 
82 	pthread_mutex_t mtx;
83 	pthread_mutexattr_t ma[4];
84 	pthread_mutexattr_t *pma[5];
85 
86 	int ret = 0;
87 	int i;
88 	int retini[5] = { -1, -1, -1, -1, -1 };
89 	int retdtr[5] = { -1, -1, -1, -1, -1 };
90 
91 	void *ptr, *ptr_prev = NULL;
92 
93 	int sz = 0;
94 	struct rlimit rl;
95 
96 	int status = 0;
97 
98 	output_init();
99 
100 	child = fork();
101 
102 	if (child == (pid_t) - 1) {
103 		UNRESOLVED(errno, "Fork failed");
104 	}
105 
106 	if (child != 0) {	/* We are the father */
107 		if (child != waitpid(child, &status, 0)) {
108 			UNRESOLVED(errno, "Waitpid failed");
109 		}
110 
111 		if (WIFSIGNALED(status)) {
112 			UNRESOLVED(WTERMSIG(status),
113 				   "The child process was killed.");
114 		}
115 
116 		if (WIFEXITED(status))
117 			return WEXITSTATUS(status);
118 
119 		UNRESOLVED(0, "Child process neither returned nor was killed.");
120 	}
121 
122 	/* Only the child goes further */
123 
124 	/* We initialize the different mutex attributes */
125 	for (i = 0; (i < 4) && (ret == 0); i++) {
126 		pma[i] = &ma[i];
127 		ret = pthread_mutexattr_init(pma[i]);
128 	}
129 	if (ret) {
130 		UNRESOLVED(ret, "Mutex attribute init failed");
131 	}
132 	pma[4] = NULL;
133 
134 	if ((ret = pthread_mutexattr_settype(pma[0], PTHREAD_MUTEX_NORMAL))) {
135 		UNRESOLVED(ret, "Mutex attribute NORMAL failed");
136 	}
137 	if ((ret = pthread_mutexattr_settype(pma[0], PTHREAD_MUTEX_DEFAULT))) {
138 		UNRESOLVED(ret, "Mutex attribute DEFAULT failed");
139 	}
140 	if ((ret = pthread_mutexattr_settype(pma[0], PTHREAD_MUTEX_RECURSIVE))) {
141 		UNRESOLVED(ret, "Mutex attribute RECURSIVE failed");
142 	}
143 	if ((ret = pthread_mutexattr_settype(pma[0], PTHREAD_MUTEX_ERRORCHECK))) {
144 		UNRESOLVED(ret, "Mutex attribute ERRORCHECK failed");
145 	}
146 
147 	sz = sysconf(_SC_PAGESIZE);
148 
149 	/* Limit the process memory to a small value (64Mb for example). */
150 	rl.rlim_max = 1024 * 1024 * 64;
151 	rl.rlim_cur = 1024 * 1024 * 64;
152 	if ((ret = setrlimit(RLIMIT_AS, &rl))) {
153 		UNRESOLVED(ret, "Memory limitation failed");
154 	}
155 #if VERBOSE > 1
156 	output("Ready to take over memory. Page size is %d\n", sz);
157 #endif
158 
159 	/* Allocate all available memory */
160 	while (1) {
161 		ptr = malloc(sz);	/* Allocate one page of memory */
162 		if (ptr == NULL)
163 			break;
164 #if VERBOSE > 1
165 		ret++;
166 #endif
167 		*(void **)ptr = ptr_prev;	/* Write into the allocated page */
168 		ptr_prev = ptr;
169 	}
170 #if VERBOSE > 1
171 	output("%d pages were allocated before failure\n", ret);
172 	ret = 0;
173 #endif
174 
175 	while (1) {
176 		ptr = malloc(sizeof(void *));	/* Allocate every remaining bits of memory */
177 		if (ptr == NULL)
178 			break;
179 #if VERBOSE > 1
180 		ret++;
181 #endif
182 		*(void **)ptr = ptr_prev;	/* Keep track of allocated memory */
183 		ptr_prev = ptr;
184 	}
185 #if VERBOSE > 1
186 	output("%d additional spaces were allocated before failure\n", ret);
187 	ret = 0;
188 #endif
189 	if (errno != ENOMEM)
190 		UNRESOLVED(errno, "Memory not full");
191 
192 	/* Now that memory is full, we try to initialize a mutex */
193 	for (i = 0; i < 5; i++) {
194 		retini[i] = pthread_mutex_init(&mtx, pma[i]);
195 		if (!retini[i])	/* If mutex has been initialized, we destroy it */
196 			retdtr[i] = pthread_mutex_destroy(&mtx);
197 	}
198 
199 	/* We can now free the memory */
200 	while (ptr_prev != NULL) {
201 		ptr = ptr_prev;
202 		ptr_prev = *(void **)ptr;
203 		free(ptr);
204 	}
205 
206 #if VERBOSE > 1
207 	output("Memory is released\n");
208 #endif
209 
210 	for (i = 0; i < 4; i++)
211 		pthread_mutexattr_destroy(pma[i]);
212 
213 	for (i = 0; i < 5; i++) {
214 		if (retini[i] != 0 && retini[i] != ENOMEM) {
215 			FAILED
216 			    ("Mutex init returned a wrong error code when no memory was left");
217 		}
218 
219 		if (retini[i] == 0) {
220 #if VERBOSE > 0
221 			output
222 			    ("Mutex initialization for attribute %d succeeds when memory is full\n",
223 			     i);
224 #endif
225 			if (retdtr[i] != 0) {
226 				UNRESOLVED(retdtr[i],
227 					   "Mutex destroy failed on mutex inilialized under heavy loaded memory");
228 			}
229 		}
230 #if VERBOSE > 0
231 		else {
232 			output
233 			    ("Mutex initialization for attribute %d fails with ENOMEM when memory is full\n",
234 			     i);
235 		}
236 #endif
237 	}
238 	PASSED;
239 }
240