diff --git a/kernel/syscall.c b/kernel/syscall.c index 270c8b75..9ede02a9 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -1346,8 +1346,8 @@ void terminate(int rc, int sig) if (child->ppid_parent == proc && child->status == PS_ZOMBIE) { - list_del(&child->hash_list); - list_del(&child->siblings_list); + list_del_init(&child->hash_list); + list_del_init(&child->siblings_list); free_child = 1; } else if (child->ppid_parent == proc) { @@ -1377,7 +1377,7 @@ void terminate(int rc, int sig) &updatelock); if (free_child) - kfree(child); + release_process(child); } mcs_rwlock_writer_unlock(&resource_set->process_hash->lock[i], &lock); diff --git a/test/issues/1349/Makefile b/test/issues/1349/Makefile new file mode 100644 index 00000000..aa623af3 --- /dev/null +++ b/test/issues/1349/Makefile @@ -0,0 +1,10 @@ +# Makefile COPYRIGHT FUJITSU LIMITED 2020 +CC?=gcc +LDFLAGS=-lpthread +CFLAGS=-Wall -O0 -ggdb3 + +all: do_fork6 +do_fork6: do_fork6.o +do_fork6.o: do_fork6.c +clean: + rm -f do_fork6.o do_fork6 diff --git a/test/issues/1349/README b/test/issues/1349/README new file mode 100644 index 00000000..bacf9533 --- /dev/null +++ b/test/issues/1349/README @@ -0,0 +1,18 @@ +========== +How to run +========== + +(1) cd +(2) Build McKernel +(3) cd /test/issues/1349 +(4) make +(5) sh ./run.sh + +============ +What to test +============ + +Check for memory leak when child processes exit without reaped by the parent's wait. + +-- +README COPYRIGHT FUJITSU LIMITED 2020 \ No newline at end of file diff --git a/test/issues/1349/do_fork6.c b/test/issues/1349/do_fork6.c new file mode 100644 index 00000000..ebf4628c --- /dev/null +++ b/test/issues/1349/do_fork6.c @@ -0,0 +1,288 @@ +/* do_fork6.c COPYRIGHT FUJITSU LIMITED 2020 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FORK1() \ + do { \ + int pid = fork(); \ + if (pid < 0) { \ + onError("fork"); \ + } else if (pid == 0) { \ + printf("grandchild\n"); \ + exit(0); \ + } \ + } while (0) + +#define FORK10() \ + do { \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + FORK1(); \ + } while (0) + +#define FORK100() \ + do { \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + FORK10(); \ + } while (0) + +enum fin_mode { + FIN_MODE_NULL, + FIN_MODE_EXIT, + FIN_MODE_WAIT, +}; + +#define MAXNUMTHREADS 256 +#define DEFAULTTIMETOWAIT 500 + +int argc; +char **argv; +int numthreads = 1; +enum fin_mode fin_mode = FIN_MODE_NULL; +int timetowait = DEFAULTTIMETOWAIT; + +struct timeval timeBeforeFork; +struct timeval timeBeforeTest; +struct timeval timeAfterTest; + +#define LAPTIME_MS(start, stop) \ + ((stop.tv_sec - start.tv_sec) * 1000 \ + + (stop.tv_usec - start.tv_usec) / 1000) + +struct Thread { + int tid; + pthread_t pthread; +} thread[MAXNUMTHREADS]; + +pthread_barrier_t barrier; + +void onError(char *message) +{ + fprintf(stderr, "%s: %s: %m\n", argv[0], message); + exit(-1); +} + +void waitChildren(void) +{ + for (;;) { + int status = 0; + pid_t pid = wait(&status); + + if (pid == -1) { + if (errno == ECHILD) { + return; + } else if (errno == EINTR) { + continue; + } + onError("wait"); + exit(-1); + } + } +} + +void examinerProcess(pid_t subject) +{ + printf("[%d] I am the examiner for %d.\n", getpid(), subject); + waitChildren(); +} + +void subjectTask(struct Thread *thread) +{ + pthread_barrier_wait(&barrier); + + gettimeofday(&timeBeforeTest, NULL); + printf("[%d] setup: %ld ms\n", + thread->tid, + LAPTIME_MS(timeBeforeFork, timeBeforeTest)); + printf("[%d] START TEST\n", thread->tid); + + FORK100(); + + printf("[%d] END FORK\n", thread->tid); + + { + struct timespec req, rem; + + req.tv_sec = timetowait / 1000; + req.tv_nsec = (timetowait % 1000) * 1000000; + if (nanosleep(&req, &rem) < 0) { + fprintf(stderr, + "nanosleep is interrupted, but ignore\n"); + } + } + + printf("[%d] FINISH CHILDREN\n", thread->tid); + switch (fin_mode) { + case FIN_MODE_EXIT: + exit(0); + break; + case FIN_MODE_WAIT: + waitChildren(); + exit(0); + break; + default: + break; + } + + printf("%d(%d) TEST FAIL OVERRUN\n", thread->tid, fin_mode); + gettimeofday(&timeAfterTest, NULL); + for (;;) + ; +} + +void subjectCleanup(void *arg) +{ + struct Thread *thread = (struct Thread *)arg; + + printf("[%d] cleanup\n", thread->tid); +} + +void *subjectThread(void *arg) +{ + struct Thread *thread = (struct Thread *)arg; + + printf("[%d] I am a %s %d, %lx %lx\n", + getpid(), + __func__, + thread->tid, + thread->pthread, + pthread_self()); + + pthread_cleanup_push(subjectCleanup, arg); + + pthread_barrier_wait(&barrier); + + subjectTask(thread); //< no return + + pthread_cleanup_pop(1); + + return NULL; +} + +void createThreads(void) +{ + if (pthread_barrier_init(&barrier, NULL, numthreads)) { + onError("pthread_barrier_init fail"); + } + + int i; + + for (i = 1; i < numthreads; i++) { + int rval; + + thread[i].tid = i; + rval = pthread_create(&thread[i].pthread, + NULL, + subjectThread, + &thread[i]); + if (rval) { + onError("pthread_create fail"); + } + } + thread[0].tid = 0; + thread[0].pthread = pthread_self(); + subjectThread(&thread[0]); +} + +int main(int _argc, char **_argv) +{ + pid_t pid; + + argc = _argc; + argv = _argv; + + printf("DO FORK6\n"); + + int i; + + for (i = 1; i < argc; i++) { + if (strcmp("-nt", argv[i]) == 0) { + i++; + if (i < argc) { + numthreads = atoi(argv[i]); + continue; + } + fprintf(stderr, "%s: num threads required\n", argv[0]); + exit(-1); + } + if (strcmp("-mode-exit", argv[i]) == 0) { + fin_mode = FIN_MODE_EXIT; + continue; + } + if (strcmp("-mode-wait", argv[i]) == 0) { + fin_mode = FIN_MODE_WAIT; + continue; + } + if (strcmp("-t", argv[i]) == 0) { + i++; + if (i < argc) { + timetowait = atoi(argv[i]); + continue; + } + } + fprintf(stderr, + "Usage: %s" + " -nt " + " -t