1 /*
2  * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil).
3  * You may copy, distribute, and use this software as long as this
4  * copyright statement is not removed.
5  */
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include "malloc.h"
9 #include "tostring.h"
10 
11 /*
12  * Function:	malloc()
13  *
14  * Purpose:	memory allocator
15  *
16  * Arguments:	size	- size of data area needed
17  *
18  * Returns:	pointer to allocated area, or NULL if unable
19  *		to allocate addtional data.
20  *
21  * Narrative:
22  *
23  */
24 #ifndef lint
25 static
26 char rcs_hdr[] = "$Id: malloc.c,v 1.2 2006-07-25 10:08:36 rt Exp $";
27 #endif
28 
29 extern int	  malloc_checking;
30 char		* malloc_data_start;
31 char		* malloc_data_end;
32 struct mlist 	* malloc_end;
33 int		  malloc_errfd = 2;
34 int		  malloc_errno;
35 int		  malloc_fatal_level = M_HANDLE_CORE;
36 struct mlist	  malloc_start;
37 int		  malloc_warn_level;
38 void		  malloc_memset();
39 
40 char *
malloc(size)41 malloc(size)
42 	unsigned int	  size;
43 {
44 	char		* func = "malloc";
45 	char		* getenv();
46 	void		  malloc_fatal();
47 	void		  malloc_init();
48 	void		  malloc_split();
49 	void		  malloc_warning();
50 	unsigned int	  need;
51 	struct mlist	* oldptr;
52 	struct mlist	* ptr;
53 	char		* sbrk();
54 
55 	/*
56 	 * If this is the first call to malloc...
57 	 */
58 	if( malloc_data_start == (char *) 0 )
59 	{
60 		malloc_init();
61 	}
62 
63 	/*
64 	 * If malloc chain checking is on, go do it.
65 	 */
66 	if( malloc_checking )
67 	{
68 		(void) malloc_chain_check(1);
69 	}
70 
71 	/*
72 	 * always make sure there is at least on extra byte in the malloc
73 	 * area so that we can verify that the user does not overrun the
74 	 * data area.
75 	 */
76 	size++;
77 
78 	/*
79 	 * Now look for a free area of memory of size bytes...
80 	 */
81 	oldptr = NULL;
82 	for(ptr = &malloc_start; ; ptr = ptr->next)
83 	{
84 		/*
85 		 * Since the malloc chain is a forward only chain, any
86 		 * pointer that we get should always be positioned in
87 		 * memory following the previous pointer.  If this is not
88 		 * so, we must have a corrupted chain.
89 		 */
90 		if( ptr )
91 		{
92 			if( ptr<oldptr )
93 			{
94 				malloc_errno = M_CODE_CHAIN_BROKE;
95 				malloc_fatal(func);
96 				return(NULL);
97 			}
98 			oldptr = ptr;
99 		}
100 		else if( oldptr != malloc_end )
101 		{
102 			/*
103 			 * This should never happen.  If it does, then
104 			 * we got a real problem.
105 			 */
106 			malloc_errno = M_CODE_NO_END;
107 			malloc_fatal(func);
108 			return(NULL);
109 		}
110 
111 
112 		/*
113 		 * if this element is already in use...
114 		 */
115 		if( ptr && ((ptr->flag & M_INUSE) != 0) )
116 		{
117 			continue;
118 		}
119 
120 		/*
121 		 * if there isn't room for this block..
122 		 */
123 		if( ptr && (ptr->s.size < size) )
124 		{
125 			continue;
126 		}
127 
128 		/*
129 		 * If ptr is null, we have run out of memory and must sbrk more
130 		 */
131 		if( ptr == NULL )
132 		{
133 			need = (size + M_SIZE) * (size > 10*1024 ? 1:2);
134 			if( need < M_BLOCKSIZE )
135 			{
136 				need = M_BLOCKSIZE;
137 			}
138 			else if( need & (M_BLOCKSIZE-1) )
139 			{
140 				need &= ~(M_BLOCKSIZE-1);
141 				need += M_BLOCKSIZE;
142 			}
143 			ptr = (struct mlist *) sbrk((int)need);
144 			if( ptr == (struct mlist *) -1 )
145 			{
146 				malloc_errno = M_CODE_NOMORE_MEM;
147 				malloc_fatal(func);
148 			}
149 			malloc_data_end = sbrk((int)0);
150 
151 			ptr->prev   = oldptr;
152 			ptr->next   = (struct mlist *) 0;
153 			ptr->s.size = need - M_SIZE;
154 			ptr->flag  = M_MAGIC;
155 
156 			oldptr->next = ptr;
157 			malloc_end = ptr;
158 
159 
160 		} /* if( ptr ==... */
161 
162 		/*
163 	 	 * Now ptr points to a memory location that can store
164 		 * this data, so lets go to work.
165 		 */
166 
167 		ptr->r_size = size;		/* save requested size	*/
168 		ptr->flag |= M_INUSE;
169 
170 		/*
171 	 	 * split off unneeded data area in this block, if possible...
172 		 */
173 		malloc_split(ptr);
174 
175 		/*
176 		 * re-adjust the requested size so that it is what the user
177 		 * actually requested...
178 		 */
179 
180 		ptr->r_size--;
181 
182 		/*
183 		 * just to make sure that noone is misusing malloced
184 	 	 * memory without initializing it, lets set it to
185 		 * all '\01's.  We call local_memset() because memset()
186 		 * may be checking for malloc'd ptrs and this isn't
187 		 * a malloc'd ptr yet.
188 		 */
189 		malloc_memset(ptr->data,M_FILL,(int)ptr->s.size);
190 
191 		return( ptr->data);
192 
193 	} /* for(... */
194 
195 } /* malloc(... */
196 
197 /*
198  * Function:	malloc_split()
199  *
200  * Purpose:	to split a malloc segment if there is enough room at the
201  *		end of the segment that isn't being used
202  *
203  * Arguments:	ptr	- pointer to segment to split
204  *
205  * Returns:	nothing of any use.
206  *
207  * Narrative:
208  *		get the needed size of the module
209  * 		round the size up to appropriat boundry
210  *		calculate amount of left over space
211  *		if there is enough left over space
212  *		    create new malloc block out of remainder
213  *		    if next block is free
214  *			join the two blocks together
215  *		    fill new empty block with free space filler
216  * 		    re-adjust pointers and size of current malloc block
217  *
218  *
219  *
220  * Mod History:
221  *   90/01/27	cpcahil		Initial revision.
222  */
223 void
malloc_split(ptr)224 malloc_split(ptr)
225 	struct mlist		* ptr;
226 {
227 	extern struct mlist	* malloc_end;
228 	void			  malloc_join();
229 	int			  rest;
230 	int			  size;
231 	struct mlist		* tptr;
232 
233 	size = ptr->r_size;
234 
235 	/*
236 	 * roundup size to the appropriate boundry
237 	 */
238 
239 	M_ROUNDUP(size);
240 
241 	/*
242 	 * figure out how much room is left in the array.
243 	 * if there is enough room, create a new mlist
244 	 *     structure there.
245 	 */
246 
247 	if( ptr->s.size > size )
248 	{
249 		rest = ptr->s.size - size;
250 	}
251 	else
252 	{
253 		rest = 0;
254 	}
255 
256 	if( rest > (M_SIZE+M_RND) )
257 	{
258 		tptr = (struct mlist *) (ptr->data+size);
259 		tptr->prev = ptr;
260 		tptr->next = ptr->next;
261 		tptr->flag = M_MAGIC;
262 		tptr->s.size = rest - M_SIZE;
263 
264 		/*
265 		 * If possible, join this segment with the next one
266 		 */
267 
268 		malloc_join(tptr, tptr->next,0,0);
269 
270 		if( tptr->next )
271 		{
272 			tptr->next->prev = tptr;
273 		}
274 
275 		malloc_memset(tptr->data,M_FREE_FILL, (int)tptr->s.size);
276 
277 		ptr->next = tptr;
278 		ptr->s.size = size;
279 
280 		if( malloc_end == ptr )
281 		{
282 			malloc_end = tptr;
283 		}
284 	}
285 
286 } /* malloc_split(... */
287 
288 /*
289  * Function:	malloc_join()
290  *
291  * Purpose:	to join two malloc segments together (if possible)
292  *
293  * Arguments:	ptr	- pointer to segment to join to.
294  * 		nextptr	- pointer to next segment to join to ptr.
295  *
296  * Returns:	nothing of any values.
297  *
298  * Narrative:
299  *
300  * Mod History:
301  *   90/01/27	cpcahil		Initial revision.
302  */
303 void
malloc_join(ptr,nextptr,inuse_override,fill_flag)304 malloc_join(ptr,nextptr, inuse_override, fill_flag)
305 	struct mlist	* ptr;
306 	struct mlist	* nextptr;
307 	int		  inuse_override;
308 	int		  fill_flag;
309 {
310 	unsigned int	  newsize;
311 
312 	if( 	ptr     && ! (inuse_override || (ptr->flag & M_INUSE)) &&
313 		nextptr && ! (nextptr->flag & M_INUSE) &&
314 		((ptr->data+ptr->s.size) == (char *) nextptr) )
315 	{
316 		if( malloc_end == nextptr )
317 		{
318 			malloc_end = ptr;
319 		}
320 		ptr->next = nextptr->next;
321 		newsize = nextptr->s.size + M_SIZE;
322 
323 		/*
324 		 * if we are to fill and this segment is in use,
325 		 *   fill in with M_FILL newly added space...
326 	 	 */
327 
328 		if(fill_flag && (ptr->flag & M_INUSE) )
329 		{
330 			malloc_memset(ptr->data+ptr->s.size,
331 				      M_FILL, (int)(nextptr->s.size + M_SIZE));
332 		}
333 
334 		ptr->s.size += newsize;
335 		if( ptr->next )
336 		{
337 			ptr->next->prev = ptr;
338 		}
339 	}
340 
341 } /* malloc_join(... */
342 
343 
344 /*
345  * The following mess is just to ensure that the versions of these functions in
346  * the current library are included (to make sure that we don't accidentaly get
347  * the libc versions. (This is the lazy man's -u ld directive)
348  */
349 
350 void free();
351 int strcmp();
352 int memcmp();
353 char	* realloc();
354 
355 void		(*malloc_void_funcs[])() =
356 {
357 	free,
358 };
359 
360 int		(*malloc_int_funcs[])() =
361 {
362 	strcmp,
363 	memcmp,
364 };
365 
366 char		* (*malloc_char_star_funcs[])() =
367 {
368 	realloc,
369 };
370 
371 /*
372  * This is malloc's own memset which is used without checking the parameters.
373  */
374 
375 void
malloc_memset(ptr,byte,len)376 malloc_memset(ptr,byte,len)
377 	char		* ptr;
378 	char		  byte;
379 	int		  len;
380 {
381 
382 	while(len-- > 0)
383 	{
384 		*ptr++ = byte;
385 	}
386 
387 } /* malloc_memset(... */
388 
389 /*
390  * Function:	malloc_fatal()
391  *
392  * Purpose:	to display fatal error message and take approrpriate action
393  *
394  * Arguments:	funcname - name of function calling this routine
395  *
396  * Returns:	nothing of any value
397  *
398  * Narrative:
399  *
400  * Notes:	This routine does not make use of any libc functions to build
401  *		and/or disply the error message.  This is due to the fact that
402  *		we are probably at a point where malloc is having a real problem
403  *		and we don't want to call any function that may use malloc.
404  */
405 void
malloc_fatal(funcname)406 malloc_fatal(funcname)
407 	char		* funcname;
408 {
409 	char		  errbuf[128];
410 	void		  exit();
411 	void		  malloc_err_handler();
412 	extern char	* malloc_err_strings[];
413 	extern int	  malloc_errno;
414 	extern int	  malloc_fatal_level;
415 	char		* s;
416 	char		* t;
417 
418 	s = errbuf;
419 	t = "Fatal error: ";
420 	while( *s = *t++)
421 	{
422 		s++;
423 	}
424 	t = funcname;
425 	while( *s = *t++)
426 	{
427 		s++;
428 	}
429 
430 	t = "(): ";
431 	while( *s = *t++)
432 	{
433 		s++;
434 	}
435 
436 	t = malloc_err_strings[malloc_errno];
437 	while( *s = *t++)
438 	{
439 		s++;
440 	}
441 
442 	*(s++) = '\n';
443 
444 	if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf))
445 	{
446 		(void) write(2,"I/O error to error file\n",(unsigned)24);
447 		exit(110);
448 	}
449 	malloc_err_handler(malloc_fatal_level);
450 
451 } /* malloc_fatal(... */
452 
453 /*
454  * Function:	malloc_warning()
455  *
456  * Purpose:	to display warning error message and take approrpriate action
457  *
458  * Arguments:	funcname - name of function calling this routine
459  *
460  * Returns:	nothing of any value
461  *
462  * Narrative:
463  *
464  * Notes:	This routine does not make use of any libc functions to build
465  *		and/or disply the error message.  This is due to the fact that
466  *		we are probably at a point where malloc is having a real problem
467  *		and we don't want to call any function that may use malloc.
468  */
469 void
malloc_warning(funcname)470 malloc_warning(funcname)
471 	char		* funcname;
472 {
473 	char		  errbuf[128];
474 	void		  exit();
475 	void		  malloc_err_handler();
476 	extern char	* malloc_err_strings[];
477 	extern int	  malloc_errno;
478 	extern int	  malloc_warn_level;
479 	char		* s;
480 	char		* t;
481 
482 	s = errbuf;
483 	t = "Warning: ";
484 	while( *s = *t++)
485 	{
486 		s++;
487 	}
488 	t = funcname;
489 	while( *s = *t++)
490 	{
491 		s++;
492 	}
493 
494 	t = "(): ";
495 	while( *s = *t++)
496 	{
497 		s++;
498 	}
499 
500 	t = malloc_err_strings[malloc_errno];
501 	while( *s = *t++)
502 	{
503 		s++;
504 	}
505 
506 	*(s++) = '\n';
507 
508 	if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf))
509 	{
510 		(void) write(2,"I/O error to error file\n",(unsigned)24);
511 		exit(110);
512 	}
513 
514 	malloc_err_handler(malloc_warn_level);
515 
516 } /* malloc_warning(... */
517 
518 /*
519  * Function:	malloc_err_handler()
520  *
521  * Purpose:	to take the appropriate action for warning and/or fatal
522  *		error conditions.
523  *
524  * Arguments:	level - error handling level
525  *
526  * Returns:	nothing of any value
527  *
528  * Narrative:
529  *
530  * Notes:	This routine does not make use of any libc functions to build
531  *		and/or disply the error message.  This is due to the fact that
532  *		we are probably at a point where malloc is having a real problem
533  *		and we don't want to call any function that may use malloc.
534  */
535 void
malloc_err_handler(level)536 malloc_err_handler(level)
537 {
538 	void		  exit();
539 	void		  malloc_dump();
540 	extern int	  malloc_errfd;
541 
542 	if( level & M_HANDLE_DUMP )
543 	{
544 		malloc_dump(malloc_errfd);
545 	}
546 
547 	switch( level & ~M_HANDLE_DUMP )
548 	{
549 		/*
550 		 * If we are to drop a core file and exit
551 		 */
552 		case M_HANDLE_ABORT:
553 			(void) abort();
554 			break;
555 
556 		/*
557 		 * If we are to exit..
558 		 */
559 		case M_HANDLE_EXIT:
560 			exit(200);
561 			break;
562 
563 #ifndef __MSDOS__
564 		/*
565 		 * If we are to dump a core, but keep going on our merry way
566 		 */
567 		case M_HANDLE_CORE:
568 			{
569 				int	  pid;
570 
571 				/*
572 				 * fork so child can abort (and dump core)
573 				 */
574 				if( (pid = fork()) == 0 )
575 				{
576 					(void) write(2,"Child dumping core\n",
577 							(unsigned)9);
578 					(void) abort();
579 				}
580 
581 				/*
582  				 * wait for child to finish dumping core
583 				 */
584 				while( wait((int *)0) != pid)
585 				{
586 				}
587 
588 				/*
589 				 * Move core file to core.pid.cnt so
590 				 * multiple cores don't overwrite each
591 				 * other.
592 				 */
593 				if( access("core",0) == 0 )
594 				{
595 					static int	  corecnt;
596 					char	  	  filenam[32];
597 					filenam[0] = 'c';
598 					filenam[1] = 'o';
599 					filenam[2] = 'r';
600 					filenam[3] = 'e';
601 					filenam[4] = '.';
602 					(void)tostring(filenam+5,getpid(),
603 						5, B_DEC, '0');
604 					filenam[10] = '.';
605 					(void)tostring(filenam+11,corecnt++,
606 						3, B_DEC, '0');
607 					filenam[14] = '\0';
608 					(void) unlink(filenam);
609 					if( link("core",filenam) == 0)
610 					{
611 						(void) unlink("core");
612 					}
613 				}
614 			}
615 #endif
616 
617 
618 		/*
619 		 * If we are to just ignore the error and keep on processing
620 		 */
621 		case M_HANDLE_IGNORE:
622 			break;
623 
624 	} /* switch(... */
625 
626 } /* malloc_err_handler(... */
627 
628