shithub: drawcpu

ref: 9b8e21a6669c6312809b174220fca59728cbb68b
dir: /kern/sysproc.c/

View raw version
#include    "u.h"
#include    "lib.h"
#include	"dat.h"
#include	"fns.h"
#include	"error.h"
#include    "user.h"
#include	"mem.h"
#include    <a.out.h>

static int
shargs(char *s, int n, char **ap, int nap)
{
	char *p;
	int i;

	if(n <= 2 || s[0] != '#' || s[1] != '!')
		return -1;
	s += 2;
	n -= 2;		/* skip #! */
	if((p = memchr(s, '\n', n)) == nil)
		return 0;
	*p = 0;
	i = tokenize(s, ap, nap-1);
	ap[i] = nil;
	return i;
}

ulong
beswal(ulong l)
{
	uchar *p;

	p = (uchar*)&l;
	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}

uvlong
beswav(uvlong v)
{
	uchar *p;

	p = (uchar*)&v;
	return ((uvlong)p[0]<<56) | ((uvlong)p[1]<<48) | ((uvlong)p[2]<<40)
				  | ((uvlong)p[3]<<32) | ((uvlong)p[4]<<24)
				  | ((uvlong)p[5]<<16) | ((uvlong)p[6]<<8)
				  | (uvlong)p[7];
}

void
evenaddr(uintptr addr)
{
	if(addr & 3){
		postnote(up, 1, "sys: odd address", NDebug);
		error(Ebadarg);
	}
}

Segment *
seg(Proc *p, uintptr addr, int dolock)
{
	Segment **s, **et, *n;

	et = &p->seg[NSEG];
	for(s = p->seg; s < et; s++) {
		if((n = *s) == nil)
			continue;
		print("Here %x %x %x\n", addr, n->base, n->top);
		if(addr >= n->base && addr < n->top) {
			if(dolock == 0)
				return n;

			qlock(&n->ql);
			if(addr >= n->base && addr < n->top)
				return n;
			qunlock(&n->ql);
		}
	}

	return nil;
}

int
okaddr(uintptr addr, ulong len, int wr)
{
	Segment *s;

	if((long)len >= 0 && len <= -addr)
		for(;;) {
			s = seg(up, addr, 0);
			if(s == nil)
				print("Nil seg\n");
			if(s == nil || (wr && (s->type&SG_RONLY)))
				break;
			print("Here\n");
			if((ulong)addr+len > s->top) {
				len -= s->top - (ulong)addr;
				addr = s->top;
				print("Looping\n");
				continue;
			}
			print("Good!\n");
			return 1;
		}

	return 0;
}

void
validaddr(uintptr addr, ulong len, int wr)
{
	if(!okaddr(addr, len, wr)) {
		pprint("suicide: invalid address\n");
		postnote(up, 1, "sys: bad address in syscall", NDebug);
		error(Ebadarg);
	}
}

/*
 * &s[0] is known to be a valid address.
 */
void*
vmemchr(void *s, int c, ulong n)
{
	uintptr a;
	ulong m, i;
	void *t;

	i = n;
	a = (uintptr)s;
	for(;;){
		m = BY2PG - (a & (BY2PG-1));
		if(i <= m)
			break;
		/* spans pages; handle this page */
		t = memchr((void*)a, c, m);
		if(t != nil)
			return t;
		a += m;
		i -= m;
		if(a < KZERO)
			validaddr(a, 1, 0);
	}
print(" returning\n");
	/* fits in one page */
	return memchr((void*)a, c, i);
}

#undef read
int
sysexec(va_list list)
{
	union {
		struct {
			Exec ex;
			uvlong	hdr[1];
		} ehdr;
		char buf[256];
	} u;
	char line[256];
	char *progarg[32+1];
	volatile char *args, *file0, *elem;
	char **argv, **argp, **argp0;
	char *a, *e, *file;
	Chan *tc;
	ulong magic, nbytes, nargs;
	uintptr t, d, b, entry, text, align, data, bss, bssend;
	int n, indir;

	args = elem = nil;
	file0 = va_arg(list, char*);
	validaddr((uintptr)file0, 1, 0);
	argp0 = va_arg(list, char**);
	evenaddr((uintptr)argp0);
	validaddr((uintptr)argp0, 2*BY2WD, 0);
	if(*argp0 == nil)
		error(Ebadarg);
	file0 = validnamedup(file0, 1);
	if(waserror()){
		free(file0);
		free(elem);
		free(args);
		/* Disaster after commit */
		if(up->seg[SSEG] == nil)
			pexit(up->errstr, 1);
		nexterror();
	}

	align = BY2PG-1;
	indir = 0;
	file = file0;
	for(;;){
		tc = namec(file, Aopen, OEXEC, 0);
		if(waserror()){
			cclose(tc);
			nexterror();
		}
		if(!indir)
			kstrdup(&elem, up->genbuf);

		n = devtab[tc->type]->read(tc, u.buf, sizeof(u.buf), 0);
		if (n >= sizeof(Exec)){
			magic = beswal(u.ehdr.ex.magic);
			if(magic & AOUT_MAGIC) {
				if(magic & HDR_MAGIC){
					if(n < sizeof(u.ehdr))
						error(Ebadexec);
					entry = beswav(u.ehdr.hdr[0]);
					text = UTZERO+sizeof(u.ehdr);
				} else {
					entry = beswal(u.ehdr.ex.entry);
					text = UTZERO+sizeof(Exec);
				}
				if(entry < text)
					error(Ebadexec);

				text += beswal(u.ehdr.ex.text);
				// Shouldn't have to cast to ulong here, unsure why it's needed
				if((ulong)text <= (ulong)entry || (ulong)text >= (USTKTOP-USTKSIZE))
					error(Ebadexec);
				switch(magic){
				case S_MAGIC:	/* 2MB segment alignment for amd64 */
					align = 0x1fffff;
					break;
				case P_MAGIC:	/* 16K segment alignment for spim */
				case V_MAGIC:	/* 16K segment alignment for mips */
					align = 0x3fff;
					break;
				case R_MAGIC:	/* 64K segment alignment for arm64 */
					align = 0xffff;
					break;
				}

				break; /* for binary */
			}
		}
		if(indir++)
			error(Ebadexec);

		/* Process #!/bin/sh args */
		memmove(line, u.buf, n);
		n = shargs(line, n, progarg, nelem(progarg));
		if(n < 1)
			error(Ebadexec);

		/* First arg becomes complete file name */
		progarg[n++] = file;
		progarg[n] = nil;
		argp0++;
		file = progarg[0];
		progarg[0] = elem;
		poperror();
		cclose(tc);
	}

	t = (text+align) & ~align;
	text -= UTZERO;
	data = beswal(u.ehdr.ex.data);
	bss = beswal(u.ehdr.ex.bss);
	align = BY2PG-1;
	d = (t + data + align) & ~align;
	bssend = t + data + bss;
	b = (bssend + align) & ~align;
	if(t >= (ulong)(USTKTOP-USTKSIZE) || d >= (ulong)(USTKTOP-USTKSIZE) || b >= (ulong)(USTKTOP-USTKSIZE))
		error(Ebadexec);

	/* Args: pass 1: count */
	nbytes = sizeof(Tos);
	nargs = 0;
	if(indir){
		argp = progarg;
		while(*argp != nil){
			a = *argp++;
			nbytes += strlen(a)+1;
			nargs++;
		}
	}
	argp = argp0;

	while(*argp != nil){
		a = *argp++;

		if(((uintptr)argp&(BY2PG-1)) < BY2WD)
			validaddr((uintptr)argp, BY2WD, 0);
		validaddr((uintptr)a, 1, 0);

		e = vmemchr(a, 0, USTKSIZE);
		if(e == nil)
			error(Ebadarg);

		nbytes += (e - a) + 1;
		if(nbytes >= USTKSIZE)
			error(Enovmem);
		nargs++;
	}

	print("%d args\n", nargs);
	
	// End of the road cleanup
	poperror(); // tc
	cclose(tc);

	poperror(); // file0
	free(file0);

    return 0;
}