 298b41b523
			
		
	
	
		298b41b523
		
	
	
	
	
		
			
			if an exec() fails partway through reading in the sections, the target process is already gone and a defunct process remains. sanity checking the binary beforehand helps that. test10 mutilates binaries and exec()s them on purpose; making an exec() fail cleanly in such cases seems like acceptable behaviour. fixes test10 on ARM. Change-Id: I1ed9bb200ce469d4d349073cadccad5503b2fcb0
		
			
				
	
	
		
			214 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "inc.h"
 | |
| #include <a.out.h>
 | |
| #include <assert.h>
 | |
| #include <libexec.h>
 | |
| #include <machine/vmparam.h>
 | |
| 
 | |
| static int do_exec(int proc_e, char *exec, size_t exec_len, char *progname,
 | |
| 	char *frame, int frame_len);
 | |
| static int exec_restart(int proc_e, int result, vir_bytes pc);
 | |
| static int read_seg(struct exec_info *execi, off_t off,
 | |
|         off_t seg_addr, size_t seg_bytes);
 | |
| static int exec_restart(int proc_e, int result, vir_bytes pc);
 | |
| 
 | |
| /* Array of loaders for different object formats */
 | |
| static struct exec_loaders {
 | |
| 	libexec_exec_loadfunc_t load_object;
 | |
| } const exec_loaders[] = {
 | |
| 	{ libexec_load_elf },
 | |
| 	{ NULL }
 | |
| };
 | |
| 
 | |
| int srv_execve(int proc_e, char *exec, size_t exec_len, char **argv,
 | |
| 	char **UNUSED(Xenvp))
 | |
| {
 | |
| 	char * const *ap;
 | |
| 	char * const *ep;
 | |
| 	char *frame;
 | |
| 	char **vp;
 | |
| 	char *sp, *progname;
 | |
| 	size_t argc;
 | |
| 	size_t frame_size;
 | |
| 	size_t string_off;
 | |
| 	size_t n;
 | |
| 	int ov;
 | |
| 	int r;
 | |
| 
 | |
| 	/* Assumptions: size_t and char *, it's all the same thing. */
 | |
| 
 | |
| 	/* Create a stack image that only needs to be patched up slightly
 | |
| 	 * by the kernel to be used for the process to be executed.
 | |
| 	 */
 | |
| 
 | |
| 	ov= 0;			/* No overflow yet. */
 | |
| 	frame_size= 0;		/* Size of the new initial stack. */
 | |
| 	string_off= 0;		/* Offset to start of the strings. */
 | |
| 	argc= 0;		/* Argument count. */
 | |
| 
 | |
| 	for (ap= argv; *ap != NULL; ap++) {
 | |
| 		n = sizeof(*ap) + strlen(*ap) + 1;
 | |
| 		frame_size+= n;
 | |
| 		if (frame_size < n) ov= 1;
 | |
| 		string_off+= sizeof(*ap);
 | |
| 		argc++;
 | |
| 	}
 | |
| 
 | |
| 	/* Add an argument count and two terminating nulls. */
 | |
| 	frame_size+= sizeof(argc) + sizeof(*ap) + sizeof(*ep);
 | |
| 	string_off+= sizeof(argc) + sizeof(*ap) + sizeof(*ep);
 | |
| 
 | |
| 	/* Align. */
 | |
| 	frame_size= (frame_size + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
 | |
| 
 | |
| 	/* The party is off if there is an overflow. */
 | |
| 	if (ov || frame_size < 3 * sizeof(char *)) {
 | |
| 		errno= E2BIG;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Allocate space for the stack frame. */
 | |
| 	frame = (char *) malloc(frame_size);
 | |
| 	if (!frame) {
 | |
| 		errno = E2BIG;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Set arg count, init pointers to vector and string tables. */
 | |
| 	* (size_t *) frame = argc;
 | |
| 	vp = (char **) (frame + sizeof(argc));
 | |
| 	sp = frame + string_off;
 | |
| 
 | |
| 	/* Load the argument vector and strings. */
 | |
| 	for (ap= argv; *ap != NULL; ap++) {
 | |
| 		*vp++= (char *) (sp - frame);
 | |
| 		n= strlen(*ap) + 1;
 | |
| 		memcpy(sp, *ap, n);
 | |
| 		sp+= n;
 | |
| 	}
 | |
| 	*vp++= NULL;
 | |
| 
 | |
| #if 0
 | |
| 	/* Load the environment vector and strings. */
 | |
| 	for (ep= envp; *ep != NULL; ep++) {
 | |
| 		*vp++= (char *) (sp - frame);
 | |
| 		n= strlen(*ep) + 1;
 | |
| 		memcpy(sp, *ep, n);
 | |
| 		sp+= n;
 | |
| 	}
 | |
| #endif
 | |
| 	*vp++= NULL;
 | |
| 
 | |
| 	/* Padding. */
 | |
| 	while (sp < frame + frame_size) *sp++= 0;
 | |
| 
 | |
| 	(progname=strrchr(argv[0], '/')) ? progname++ : (progname=argv[0]);
 | |
| 	r = do_exec(proc_e, exec, exec_len, progname, frame, frame_size);
 | |
| 
 | |
| 	/* Return the memory used for the frame and exit. */
 | |
| 	free(frame);
 | |
| 	return r;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int do_exec(int proc_e, char *exec, size_t exec_len, char *progname,
 | |
| 	char *frame, int frame_len)
 | |
| {
 | |
| 	int r;
 | |
| 	vir_bytes vsp;
 | |
| 	struct exec_info execi;
 | |
| 	int i;
 | |
| 
 | |
| 	memset(&execi, 0, sizeof(execi));
 | |
| 
 | |
| 	execi.stack_high = kinfo.user_sp;
 | |
| 	execi.stack_size = DEFAULT_STACK_LIMIT;
 | |
| 	execi.proc_e = proc_e;
 | |
| 	execi.hdr = exec;
 | |
| 	execi.filesize = execi.hdr_len = exec_len;
 | |
| 	strncpy(execi.progname, progname, PROC_NAME_LEN-1);
 | |
| 	execi.progname[PROC_NAME_LEN-1] = '\0';
 | |
| 	execi.frame_len = frame_len;
 | |
| 
 | |
| 	/* callback functions and data */
 | |
| 	execi.copymem = read_seg;
 | |
| 	execi.clearproc = libexec_clearproc_vm_procctl;
 | |
| 	execi.clearmem = libexec_clear_sys_memset;
 | |
| 	execi.allocmem_prealloc = libexec_alloc_mmap_prealloc;
 | |
| 	execi.allocmem_ondemand = libexec_alloc_mmap_ondemand;
 | |
| 
 | |
| 	for(i = 0; exec_loaders[i].load_object != NULL; i++) {
 | |
| 	    r = (*exec_loaders[i].load_object)(&execi);
 | |
| 	    /* Loaded successfully, so no need to try other loaders */
 | |
| 	    if (r == OK) break;
 | |
| 	}
 | |
| 
 | |
| 	/* No exec loader could load the object */
 | |
| 	if (r != OK) {
 | |
| 	    printf("RS: do_exec: loading error %d\n", r);
 | |
| 	    return r;
 | |
| 	}
 | |
| 
 | |
| 	/* Inform PM */
 | |
|         if((r = libexec_pm_newexec(execi.proc_e, &execi)) != OK)
 | |
| 		return r;
 | |
| 
 | |
| 	/* Patch up stack and copy it from RS to new core image. */
 | |
| 	vsp = execi.stack_high;
 | |
| 	vsp -= frame_len;
 | |
| 	libexec_patch_ptr(frame, vsp);
 | |
| 	r = sys_datacopy(SELF, (vir_bytes) frame,
 | |
| 		proc_e, (vir_bytes) vsp, (phys_bytes)frame_len);
 | |
| 	if (r != OK) {
 | |
| 		printf("do_exec: copying out new stack failed: %d\n", r);
 | |
| 		exec_restart(proc_e, r, execi.pc);
 | |
| 		return r;
 | |
| 	}
 | |
| 
 | |
| 	return exec_restart(proc_e, OK, execi.pc);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				exec_restart				     *
 | |
|  *===========================================================================*/
 | |
| static int exec_restart(int proc_e, int result, vir_bytes pc)
 | |
| {
 | |
| 	int r;
 | |
| 	message m;
 | |
| 
 | |
| 	m.m_type= EXEC_RESTART;
 | |
| 	m.EXC_RS_PROC= proc_e;
 | |
| 	m.EXC_RS_RESULT= result;
 | |
| 	m.EXC_RS_PC= (void*)pc;
 | |
| 	r= sendrec(PM_PROC_NR, &m);
 | |
| 	if (r != OK)
 | |
| 		return r;
 | |
| 	return m.m_type;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *                             read_seg                                     *
 | |
|  *===========================================================================*/
 | |
| static int read_seg(
 | |
| struct exec_info *execi,       /* various data needed for exec */
 | |
| off_t off,                     /* offset in file */
 | |
| off_t seg_addr,            /* address to load segment */
 | |
| size_t seg_bytes           /* how much is to be transferred? */
 | |
| )
 | |
| {
 | |
| /*
 | |
|  * The byte count on read is usually smaller than the segment count, because
 | |
|  * a segment is padded out to a click multiple, and the data segment is only
 | |
|  * partially initialized.
 | |
|  */
 | |
| 
 | |
|   int r;
 | |
| 
 | |
|   if (off+seg_bytes > execi->hdr_len) return ENOEXEC;
 | |
|   if((r= sys_vircopy(SELF, ((vir_bytes)execi->hdr)+off,
 | |
|   	execi->proc_e, seg_addr, seg_bytes)) != OK) {
 | |
| 	printf("RS: exec read_seg: copy 0x%x bytes into %i at 0x%08x failed: %i\n",
 | |
| 		seg_bytes, execi->proc_e, seg_addr, r);
 | |
|   }
 | |
|   return r;
 | |
| }
 |