| /* Test of external malloc, calloc, realloc and free capability */ |
| |
| #if 1 |
| #include "test.h" |
| #include "usctest.h" |
| #else |
| enum {TPASS, TFAIL, TBROK, TINFO}; |
| #define tst_resm(xxx, yyy...) printf(yyy), printf(" RES %d\n", xxx) |
| #define tst_brkm(xxx, yyy, zzz...) printf(zzz), printf(" RES %d\n", xxx) |
| #define tst_exit() |
| int Tst_count; |
| #endif |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <strings.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/wait.h> |
| |
| const char *TCID = "malloc"; /* Test program identifier. */ |
| int TST_TOTAL = 2; /* Total number of test cases. */ |
| extern int Tst_count; /* Test Case counter for tst_* routines */ |
| |
| /* Main test. |
| Verbose mode if argc > 0 |
| Note that malloc and friends are also called by Cygwin before main, |
| and that malloc can call getenv. */ |
| |
| int malloc_error = 0, realloc_error = 0, free_error = 0; |
| int calloc_count = 0, malloc_count = 0, realloc_count = 0, free_count = 0; |
| |
| void |
| cleanup (void) |
| { |
| tst_exit(); |
| } |
| |
| int |
| syncwithchild (pid_t pid, int expected_exit_status) |
| { |
| int status; |
| |
| if (waitpid (pid, &status, 0) != pid) |
| { |
| tst_brkm (TBROK, cleanup, "Wait for child: %s", strerror (errno)); |
| return 1; |
| } |
| if (!WIFEXITED (status)) |
| { |
| tst_brkm (TBROK, cleanup, "Child had abnormal exit"); |
| return 1; |
| } |
| if (WEXITSTATUS (status) != expected_exit_status) |
| { |
| tst_brkm (TFAIL, cleanup, "Child had exit status %d != %d", |
| WEXITSTATUS (status), expected_exit_status); |
| return 1; |
| } |
| return 0; |
| } |
| |
| #if 0 |
| void * mallocX (size_t size); |
| void * callocX (size_t nmemb, size_t size); |
| void * reallocX (void * ptr, size_t size); |
| void freeX(void *); |
| |
| #define malloc mallocX |
| #define calloc callocX |
| #define realloc reallocX |
| #define free freeX |
| #endif |
| |
| int main (int argc, char * argv[]) |
| { |
| void * ptr; |
| int error = 0; |
| pid_t pid; |
| |
| Tst_count = 0; |
| |
| tst_resm(TINFO, "Testing if external malloc works. ppid %d", getppid()); |
| |
| ptr = malloc (16); |
| ptr = calloc (1, 16); |
| ptr = realloc (ptr, 24); |
| free (ptr); |
| |
| error = (malloc_count == 0 || calloc_count == 0 || realloc_count == 0 || free_count == 0); |
| |
| if (error || argc > 1) |
| { |
| printf ("malloc_count %d, calloc_count %d, realloc_count %d, free_count %d\n", |
| malloc_count, calloc_count, realloc_count, free_count); |
| printf ("malloc_error %d, realloc_error %d, free_error %d\n", |
| malloc_error, realloc_error, free_error); |
| } |
| tst_resm (!error ? TPASS : TFAIL, "Running in pid %d", getpid()); |
| |
| /* If run from Windows, run also from Cygwin */ |
| if (getppid() == 1) |
| { |
| tst_resm(TINFO, "Ready to test if malloc works from Cygwin"); |
| |
| if ((pid = fork()) == 0) |
| { |
| tst_resm(TINFO, "Ready to exec with pid %d\n", getpid()); |
| error = execl(argv[0], argv[0], argc > 1? argv[1]:NULL, NULL); |
| exit(error); |
| } |
| else if (pid < 0) |
| tst_brkm (TBROK, cleanup, "Fork failed: %s", strerror (errno)); |
| else |
| { |
| error = syncwithchild (pid, 0); |
| tst_resm (!error ? TPASS : TFAIL, "Running in pid %d", pid); |
| } |
| } |
| |
| tst_exit (); |
| } |
| |
| /**************************************** |
| Actual malloc & friends implementation |
| ****************************************/ |
| |
| typedef unsigned long long ull; |
| |
| #define SIZE (1024*1024ULL) /* long long */ |
| ull buffer[SIZE]; |
| ull * current = buffer; |
| |
| static int is_valid (void * ptr) |
| { |
| unsigned int iptr = (unsigned int) ptr; |
| ull * ullptr = (ull *) ptr; |
| |
| iptr = (iptr / sizeof(ull)) * sizeof(ull); |
| if (iptr != (int) ptr) |
| return 0; |
| if (--ullptr < buffer || ullptr[0] > SIZE || ullptr + ullptr[0] > current) |
| return 0; |
| return 1; |
| } |
| |
| void * malloc (size_t size) |
| { |
| ull llsize = (size + 2 * sizeof (ull) - 1) / sizeof (ull); |
| static char * envptr; |
| void * ret; |
| |
| /* Make sure getenv works */ |
| if (!envptr) |
| envptr = getenv ("PATH"); |
| |
| malloc_count++; |
| if (current + llsize >= buffer + SIZE) |
| { |
| malloc_error++; |
| errno = ENOMEM; |
| return NULL; |
| } |
| *current = llsize; |
| ret = (void *) (current + 1); |
| current += llsize; |
| |
| return ret; |
| } |
| |
| void * calloc (size_t nmemb, size_t size) |
| { |
| calloc_count++; |
| void * ptr = malloc (nmemb * size); |
| malloc_count--; |
| if (ptr) |
| memset(ptr, 0, nmemb * size); |
| return ptr; |
| } |
| |
| void * realloc (void * ptr, size_t size) |
| { |
| const ull ullsize = (size + 2 * sizeof (ull) - 1) / sizeof (ull); |
| ull * const ullptr = (ull *) ptr; |
| void * newptr; |
| |
| realloc_count++; |
| |
| if (ptr) |
| { |
| if (!is_valid (ptr)) |
| { |
| realloc_error++; |
| errno = ENOMEM; |
| return NULL; |
| } |
| if (ullptr[-1] >= ullsize) |
| return ptr; |
| } |
| |
| newptr = malloc (size); |
| malloc_count--; |
| |
| if (ptr && newptr) |
| memcpy (newptr, ptr, size); |
| |
| return newptr; |
| } |
| |
| void free (void * x) |
| { |
| free_count++; |
| if (x && ! is_valid (x)) |
| free_error++; |
| } |
| |