Subject: kern/1767: unlink of a running program's text image causes system crash
To: None <gnats-bugs@gnats.netbsd.org>
From: Tor Egge <tegge@idt.unit.no>
List: netbsd-bugs
Date: 11/16/1995 13:08:56
>Number:         1767
>Category:       kern
>Synopsis:       unlink of a running program's text image causes system crash
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Nov 16 07:20:04 1995
>Last-Modified:
>Originator:     Tor Egge
>Organization:
Norwegian University of Technology and Science
>Release:        NetBSD-current, from Nov 13th
>Environment:
System: NetBSD ikke.idt.unit.no 1.1_ALPHA NetBSD 1.1_ALPHA (TEGGE) #3: Mon Nov 13 12:37:46 MET 1995 root@:/usr/src/sys/arch/i386/compile/TEGGE i386


>Description:
	Resources seem to be freed in the wrong order in sys_wait4.
	Credentials are freed, then the call to vrele may block.
	The process is still in the process table, but without credentials.
	The sysctl_doproc routine implicitly called by kvm_getprocs in
	top dereferences pointers into freed space, and pointers in
	this free space which no may point anywhere.

>How-To-Repeat:
	Unpack the included shell archive, and compile the programs bad4 and
	orig on an NFS mounted file system. Run top with update each second to
	cause repeated traversals of the process table.  Run the bad4 program
	with a suitable argument (e.g. bad4 40), while top is still running.
	If the system has not crashed yet, repeat the previous step a couple of
	times.

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	bad4.c
#	orig.c
#
echo x - bad4.c
sed 's/^X//' >bad4.c << 'END-of-bad4.c'
X#include <sys/types.h>
X#include <stdio.h>
X
Xint main(int argc,char *argv[])
X{
X  int i;
X  int nchilds;
X  int res;
X  char buf[100];
X
X  if (argc<2) {
X    fprintf(stderr,"usage: crashme numchildren\n");
X    exit(1);
X  }
X  nchilds=atoi(argv[1]);
X  
X  if (nchilds<1 || nchilds>1000) {
X    fprintf(stderr,"numchildren not in the 1..1000 range\n");
X    exit(1);
X  }
X
X  for (i=0;i<nchilds;i++) {
X    sprintf(buf,"cp orig copy%d",i);
X    printf("[%s]\n",buf);
X    system(buf);
X  }
X
X  for (i=0;i<nchilds;i++) {
X    sprintf(buf,"copy%d",i);
X    res=fork();
X    if (res<0) exit(1);
X    if (res==0) {
X      execl(buf,buf,0);
X      perror("execl");
X      fprintf(stderr,"Fatal: Could not execute %s\n",buf);
X      exit(1);
X    }
X    usleep(20000);
X  }
X
X  for (i=0;i<nchilds ;i++) {
X    sprintf(buf,"copy%d",i);
X    printf("Unlinking %s\n",buf);
X    unlink(buf);
X  }
X  
X  i=0;
X  while (waitpid(-1,NULL,0)>0) {
X    i++;
X  }
X  printf("Waited for %d children\n",i);
X  exit(0);
X}
END-of-bad4.c
echo x - orig.c
sed 's/^X//' >orig.c << 'END-of-orig.c'
X#include <sys/types.h>
X#include <stdio.h>
X
Xmain()
X{
X  sleep(4);
X}
END-of-orig.c
exit
>Fix:
	Change the order in which resources are freed. Freeing credentials
	will never block, and is not dependent upon other resources being
	present. Thus this operation can be moved to after the process
	has been removed from the process list.

>Audit-Trail:
>Unformatted: