From 4dcb13bb4489559119d4c86741c59e4c1eace469 Mon Sep 17 00:00:00 2001
From: Rob Pike <r@golang.org>
Date: Mon, 29 Apr 2013 22:44:40 -0700
Subject: [PATCH] cmd/gc: fix some overflows in the compiler Some 64-bit fields
 were run through 32-bit words, some counts were not checked for overflow, and
 relocations must fit in 32 bits. Tests to follow.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/9033043
---
 src/cmd/5g/cgen.c      |  4 +++-
 src/cmd/5g/gsubr.c     |  5 +++--
 src/cmd/6g/cgen.c      | 10 ++++++----
 src/cmd/6g/ggen.c      |  2 +-
 src/cmd/6g/gobj.c      |  3 ++-
 src/cmd/6g/gsubr.c     |  8 +++++---
 src/cmd/6g/reg.c       |  6 +++---
 src/cmd/8g/cgen.c      |  4 +++-
 src/cmd/8g/gsubr.c     |  3 ++-
 src/cmd/gc/align.c     |  7 +++++--
 src/cmd/gc/closure.c   |  2 +-
 src/cmd/gc/gen.c       |  2 +-
 src/cmd/gc/go.h        | 12 ++++++------
 src/cmd/gc/pgen.c      | 13 +++++++++++--
 src/cmd/gc/sinit.c     |  8 ++++++--
 src/cmd/gc/subr.c      | 11 ++++++++---
 src/cmd/gc/swt.c       |  2 ++
 src/cmd/gc/typecheck.c |  3 ++-
 src/cmd/gc/unsafe.c    |  2 +-
 19 files changed, 71 insertions(+), 36 deletions(-)

diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index 0844e180f6..1620f410ad 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -680,7 +680,9 @@ agen(Node *n, Node *res)
 	case ODOT:
 		agen(nl, res);
 		// explicit check for nil if struct is large enough
-		// that we might derive too big a pointer.
+		// that we might derive too big a pointer.  If the left node
+		// was ODOT we have already done the nil check.
+		if(nl->op != ODOT)
 		if(nl->type->width >= unmappedzero) {
 			regalloc(&n1, types[tptr], N);
 			gmove(res, &n1);
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index d049ebe052..815d6fab23 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -1785,7 +1785,8 @@ sudoclean(void)
 int
 dotaddable(Node *n, Node *n1)
 {
-	int o, oary[10];
+	int o;
+	int64 oary[10];
 	Node *nn;
 
 	if(n->op != ODOT)
@@ -1816,7 +1817,7 @@ int
 sudoaddable(int as, Node *n, Addr *a, int *w)
 {
 	int o, i;
-	int oary[10];
+	int64 oary[10];
 	int64 v;
 	Node n1, n2, n3, n4, *nn, *l, *r;
 	Node *reg, *reg1;
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 32980a50b5..2eae865f33 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -568,7 +568,7 @@ agenr(Node *n, Node *a, Node *res)
 	Node n1, n2, n3, n4, n5, tmp, tmp2, nlen;
 	Prog *p1;
 	Type *t;
-	uint32 w;
+	uint64 w;
 	uint64 v;
 	int freelen;
 
@@ -883,7 +883,9 @@ agen(Node *n, Node *res)
 	case ODOT:
 		agen(nl, res);
 		// explicit check for nil if struct is large enough
-		// that we might derive too big a pointer.
+		// that we might derive too big a pointer.  If the left node
+		// was ODOT we have already done the nil check.
+		if(nl->op != ODOT)
 		if(nl->type->width >= unmappedzero) {
 			regalloc(&n1, types[tptr], res);
 			gmove(res, &n1);
@@ -1285,12 +1287,12 @@ ret:
  * or return value from function call.
  * return n's offset from SP.
  */
-int32
+int64
 stkof(Node *n)
 {
 	Type *t;
 	Iter flist;
-	int32 off;
+	int64 off;
 
 	switch(n->op) {
 	case OINDREG:
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c
index 23bb5093f0..5e426753c5 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -942,7 +942,7 @@ cgen_bmul(int op, Node *nl, Node *nr, Node *res)
 void
 clearfat(Node *nl)
 {
-	uint32 w, c, q;
+	int64 w, c, q;
 	Node n1, oldn1, ax, oldax;
 
 	/* clear a fat object */
diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c
index 508a3548ff..cdbbd5d9db 100644
--- a/src/cmd/6g/gobj.c
+++ b/src/cmd/6g/gobj.c
@@ -501,7 +501,8 @@ void
 genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
 {
 	Sym *e;
-	int c, d, o, mov, add, loaded;
+	int c, d, mov, add, loaded;
+	int64 o;
 	Prog *p;
 	Type *f;
 	
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index 7399832468..55864c34ed 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -1147,6 +1147,8 @@ naddr(Node *n, Addr *a, int canemitcode)
 		a->type = n->val.u.reg+D_INDIR;
 		a->sym = n->sym;
 		a->offset = n->xoffset;
+		if(a->offset != (int32)a->offset)
+			yyerror("offset %lld too large for OINDREG", a->offset);
 		checkoffset(a, canemitcode);
 		break;
 
@@ -1947,9 +1949,9 @@ sudoclean(void)
 int
 sudoaddable(int as, Node *n, Addr *a)
 {
-	int o, i, w;
-	int oary[10];
-	int64 v;
+	int o, i;
+	int64 oary[10];
+	int64 v, w;
 	Node n1, n2, n3, n4, *nn, *l, *r;
 	Node *reg, *reg1;
 	Prog *p1;
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index 7db6beab93..ab826d431f 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -947,9 +947,9 @@ doregbits(int r)
 }
 
 static int
-overlap(int32 o1, int w1, int32 o2, int w2)
+overlap(int64 o1, int w1, int64 o2, int w2)
 {
-	int32 t1, t2;
+	int64 t1, t2;
 
 	t1 = o1+w1;
 	t2 = o2+w2;
@@ -967,7 +967,7 @@ mkvar(Reg *r, Adr *a)
 	int i, t, n, et, z, flag;
 	int64 w;
 	uint32 regu;
-	int32 o;
+	int64 o;
 	Bits bit;
 	Node *node;
 
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c
index b88ea401bb..d54db7e629 100644
--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -740,7 +740,9 @@ agen(Node *n, Node *res)
 	case ODOT:
 		agen(nl, res);
 		// explicit check for nil if struct is large enough
-		// that we might derive too big a pointer.
+		// that we might derive too big a pointer.  If the left node
+		// was ODOT we have already done the nil check.
+		if(nl->op != ODOT)
 		if(nl->type->width >= unmappedzero) {
 			regalloc(&n1, types[tptr], res);
 			gmove(res, &n1);
diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c
index 79348a42dc..756bdd2039 100644
--- a/src/cmd/8g/gsubr.c
+++ b/src/cmd/8g/gsubr.c
@@ -2391,7 +2391,8 @@ naddr(Node *n, Addr *a, int canemitcode)
 int
 dotaddable(Node *n, Node *n1)
 {
-	int o, oary[10];
+	int o;
+	int64 oary[10];
 	Node *nn;
 
 	if(n->op != ODOT)
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c
index be9f552f67..80c65387b3 100644
--- a/src/cmd/gc/align.c
+++ b/src/cmd/gc/align.c
@@ -46,7 +46,8 @@ static vlong
 widstruct(Type *errtype, Type *t, vlong o, int flag)
 {
 	Type *f;
-	int32 w, maxalign;
+	int64 w;
+	int32 maxalign;
 	
 	maxalign = flag;
 	if(maxalign < 1)
@@ -643,7 +644,7 @@ argsize(Type *t)
 {
 	Iter save;
 	Type *fp;
-	int w, x;
+	int64 w, x;
 
 	w = 0;
 
@@ -664,5 +665,7 @@ argsize(Type *t)
 	}
 
 	w = (w+widthptr-1) & ~(widthptr-1);
+	if((int)w != w)
+		fatal("argsize too big");
 	return w;
 }
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
index 962b088452..d81c5281dd 100644
--- a/src/cmd/gc/closure.c
+++ b/src/cmd/gc/closure.c
@@ -135,7 +135,7 @@ makeclosure(Node *func)
 	NodeList *l, *body;
 	static int closgen;
 	char *p;
-	int offset;
+	vlong offset;
 
 	/*
 	 * wrap body in external function
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
index b47a04bf05..955ec2c5bb 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -836,7 +836,7 @@ cgen_slice(Node *n, Node *res)
  * <0 is pointer to next field (+1)
  */
 int
-dotoffset(Node *n, int *oary, Node **nn)
+dotoffset(Node *n, int64 *oary, Node **nn)
 {
 	int i;
 
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 1f5fb41aaa..48bcf0233f 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -156,9 +156,9 @@ struct	Type
 	int		lineno;
 
 	// TFUNC
-	uchar	thistuple;
-	uchar	outtuple;
-	uchar	intuple;
+	int	thistuple;
+	int	outtuple;
+	int	intuple;
 	uchar	outnamed;
 
 	Type*	method;
@@ -252,9 +252,7 @@ struct	Node
 	uchar	embedded;	// ODCLFIELD embedded type
 	uchar	colas;		// OAS resulting from :=
 	uchar	diag;		// already printed error about this
-	uchar	esc;		// EscXXX
 	uchar	noescape;	// func arguments do not escape
-	uchar	funcdepth;
 	uchar	builtin;	// built-in name, like len or close
 	uchar	walkdef;
 	uchar	typecheck;
@@ -269,6 +267,8 @@ struct	Node
 	uchar	dupok;	// duplicate definitions ok (for func)
 	schar	likely; // likeliness of if statement
 	uchar	hasbreak;	// has break statement
+	uint	esc;		// EscXXX
+	int	funcdepth;
 
 	// most nodes
 	Type*	type;
@@ -1103,7 +1103,7 @@ void	cgen_eface(Node* n, Node* res);
 void	cgen_slice(Node* n, Node* res);
 void	clearlabels(void);
 void	checklabels(void);
-int	dotoffset(Node *n, int *oary, Node **nn);
+int	dotoffset(Node *n, int64 *oary, Node **nn);
 void	gen(Node *n);
 void	genlist(NodeList *l);
 Node*	sysfunc(char *name);
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
index df8903bafe..82d8186b0d 100644
--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -177,8 +177,13 @@ cmpstackvar(Node *a, Node *b)
 {
 	if (a->class != b->class)
 		return (a->class == PAUTO) ? 1 : -1;
-	if (a->class != PAUTO)
-		return a->xoffset - b->xoffset;
+	if (a->class != PAUTO) {
+		if (a->xoffset < b->xoffset)
+			return -1;
+		if (a->xoffset > b->xoffset)
+			return 1;
+		return 0;
+	}
 	if ((a->used == 0) != (b->used == 0))
 		return b->used - a->used;
 	return b->type->align - a->type->align;
@@ -240,6 +245,10 @@ allocauto(Prog* ptxt)
 		stksize = rnd(stksize, n->type->align);
 		if(thechar == '5')
 			stksize = rnd(stksize, widthptr);
+		if(stksize >= (1ULL<<31)) {
+			setlineno(curfn);
+			yyerror("stack frame too large (>2GB)");
+		}
 		n->stkdelta = -stksize - n->xoffset;
 	}
 
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
index 353fc00ceb..f8c61828cf 100644
--- a/src/cmd/gc/sinit.c
+++ b/src/cmd/gc/sinit.c
@@ -801,7 +801,8 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init)
 {
 	Node *r, *a;
 	NodeList *l;
-	int nerr, b;
+	int nerr;
+	int64 b;
 	Type *t, *tk, *tv, *t1;
 	Node *vstat, *index, *value;
 	Sym *syma, *symb;
@@ -1142,7 +1143,10 @@ stataddr(Node *nam, Node *n)
 		l = getlit(n->right);
 		if(l < 0)
 			break;
-		nam->xoffset += l*n->type->width;
+		// Check for overflow.
+		if(n->type->width != 0 && MAXWIDTH/n->type->width <= l)
+			break;
+ 		nam->xoffset += l*n->type->width;
 		nam->type = n->type;
 		return 1;
 	}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index bd78fb0246..20a15bc715 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -839,7 +839,7 @@ Type*
 aindex(Node *b, Type *t)
 {
 	Type *r;
-	int bound;
+	int64 bound;
 
 	bound = -1;	// open bound
 	typecheck(&b, Erv);
@@ -1794,6 +1794,8 @@ ullmancalc(Node *n)
 		ul = ur;
 
 out:
+	if(ul > 200)
+		ul = 200; // clamp to uchar with room to grow
 	n->ullman = ul;
 }
 
@@ -2118,7 +2120,7 @@ localexpr(Node *n, Type *t, NodeList **init)
 void
 setmaxarg(Type *t)
 {
-	int32 w;
+	int64 w;
 
 	dowidth(t);
 	w = t->argwid;
@@ -3296,11 +3298,14 @@ liststmt(NodeList *l)
 int
 count(NodeList *l)
 {
-	int n;
+	vlong n;
 
 	n = 0;
 	for(; l; l=l->next)
 		n++;
+	if((int)n != n) { // Overflow.
+		yyerror("too many elements in list");
+	}
 	return n;
 }
 
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
index 5fba731402..3ad5f02a5f 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -358,6 +358,8 @@ mkcaselist(Node *sw, int arg)
 		c = c1;
 
 		ord++;
+		if((uint16)ord != ord)
+			fatal("too many cases in switch");
 		c->ordinal = ord;
 		c->node = n;
 
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 938716e215..40eecd337c 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -2350,7 +2350,8 @@ pushtype(Node *n, Type *t)
 static void
 typecheckcomplit(Node **np)
 {
-	int bad, i, len, nerr;
+	int bad, i, nerr;
+	int64 len;
 	Node *l, *n, *norig, *r, **hash;
 	NodeList *ll;
 	Type *t, *f;
diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c
index 6b26cde266..ff08c0eef1 100644
--- a/src/cmd/gc/unsafe.c
+++ b/src/cmd/gc/unsafe.c
@@ -19,7 +19,7 @@ unsafenmagic(Node *nn)
 	Node *r, *n, *base, *r1;
 	Sym *s;
 	Type *t, *tr;
-	long v;
+	vlong v;
 	Val val;
 	Node *fn;
 	NodeList *args;
-- 
2.30.9