~mcf/cproc

8f9714c3713f4060de4706e099b745d5d4e5ba1f — Michael Forney a month ago 34ae0d4
Fix type-checking of va_list arguments to varargs built-ins

If the argument was a function parameter, its type has already been
adjusted. So on x86_64, we can't just ignore the automatic
array-to-pointer conversion, since it was never a pointer to begin
with.

Instead, keep track of the adjusted va_list type, and check that
the arguments to varargs built-ins match that type.
5 files changed, 36 insertions(+), 28 deletions(-)

M cc.h
M decl.c
M expr.c
M targ.c
M type.c
M cc.h => cc.h +2 -0
@@ 415,6 415,7 @@ struct type *typecomposite(struct type *, struct type *);
struct type *typeunqual(struct type *, enum typequal *);
struct type *typecommonreal(struct type *, unsigned, struct type *, unsigned);
struct type *typepromote(struct type *, unsigned);
struct type *typeadjust(struct type *);
enum typeprop typeprop(struct type *);
struct member *typemember(struct type *, const char *, uint64_t *);



@@ 428,6 429,7 @@ extern struct type typeint, typeuint;
extern struct type typelong, typeulong;
extern struct type typellong, typeullong;
extern struct type typefloat, typedouble, typeldouble;
extern struct type *typeadjvalist;

/* targ */


M decl.c => decl.c +2 -17
@@ 618,21 618,6 @@ declarator(struct scope *s, struct qualtype base, char **name, bool allowabstrac
	return base;
}

static struct type *
adjust(struct type *t)
{
	switch (t->kind) {
	case TYPEARRAY:
		t = mkpointertype(t->base, t->qual);
		break;
	case TYPEFUNC:
		t = mkpointertype(t, QUALNONE);
		break;
	}

	return t;
}

static struct param *
parameter(struct scope *s)
{


@@ 647,7 632,7 @@ parameter(struct scope *s)
		error(&tok.loc, "parameter declaration has invalid storage-class specifier");
	t = declarator(s, t, &name, true);

	return mkparam(name, adjust(t.type), t.qual);
	return mkparam(name, typeadjust(t.type), t.qual);
}

static bool


@@ 669,7 654,7 @@ paramdecl(struct scope *s, struct param *params)
			;
		if (!p)
			error(&tok.loc, "old-style function declarator has no parameter named '%s'", name);
		p->type = adjust(t.type);
		p->type = typeadjust(t.type);
		p->qual = t.qual;
		if (tok.kind == TSEMICOLON)
			break;

M expr.c => expr.c +13 -11
@@ 642,31 642,31 @@ builtinfunc(struct scope *s, enum builtinkind kind)
	case BUILTINVAARG:
		e = mkexpr(EXPRBUILTIN, NULL);
		e->builtin.kind = BUILTINVAARG;
		e->base = mkunaryexpr(TBAND, assignexpr(s));
		if (e->base->base->type != targ->typevalist)
		e->base = assignexpr(s);
		if (!typesame(e->base->type, typeadjvalist))
			error(&tok.loc, "va_arg argument must have type va_list");
		if (!e->base->decayed)
			e->base = mkunaryexpr(TBAND, e->base);
		expect(TCOMMA, "after va_list");
		e->type = typename(s, &e->qual);
		break;
	case BUILTINVACOPY:
		e = mkexpr(EXPRASSIGN, &typevoid);
		e->assign.l = assignexpr(s);
		if (!typesame(e->assign.l->type, typeadjvalist))
			error(&tok.loc, "va_copy destination must have type va_list");
		if (e->assign.l->decayed)
			e->assign.l = e->assign.l->base;
		if (e->assign.l->type != targ->typevalist)
			error(&tok.loc, "va_copy destination must have type va_list");
		expect(TCOMMA, "after target va_list");
		e->assign.r = assignexpr(s);
		if (!typesame(e->assign.r->type, typeadjvalist))
			error(&tok.loc, "va_copy source must have type va_list");
		if (e->assign.r->decayed)
			e->assign.r = e->assign.r->base;
		if (e->assign.r->type != targ->typevalist)
			error(&tok.loc, "va_copy source must have type va_list");
		break;
	case BUILTINVAEND:
		e = assignexpr(s);
		if (e->decayed)
			e = e->base;
		if (e->type != targ->typevalist)
		if (!typesame(e->type, typeadjvalist))
			error(&tok.loc, "va_end argument must have type va_list");
		e = mkexpr(EXPRBUILTIN, &typevoid);
		e->builtin.kind = BUILTINVAEND;


@@ 674,9 674,11 @@ builtinfunc(struct scope *s, enum builtinkind kind)
	case BUILTINVASTART:
		e = mkexpr(EXPRBUILTIN, &typevoid);
		e->builtin.kind = BUILTINVASTART;
		e->base = mkunaryexpr(TBAND, assignexpr(s));
		if (e->base->base->type != targ->typevalist)
		e->base = assignexpr(s);
		if (!typesame(e->base->type, typeadjvalist))
			error(&tok.loc, "va_start argument must have type va_list");
		if (!e->base->decayed)
			e->base = mkunaryexpr(TBAND, e->base);
		expect(TCOMMA, "after va_list");
		param = assignexpr(s);
		if (param->kind != EXPRIDENT)

M targ.c => targ.c +1 -0
@@ 55,4 55,5 @@ targinit(const char *name)
	if (!targ)
		fatal("unknown target '%s'", name);
	typechar.basic.issigned = targ->signedchar;
	typeadjvalist = typeadjust(targ->typevalist);
}

M type.c => type.c +18 -0
@@ 39,6 39,8 @@ struct type typefloat   = FLTTYPE(TYPEFLOAT, 4);
struct type typedouble  = FLTTYPE(TYPEDOUBLE, 8);
struct type typeldouble = FLTTYPE(TYPELDOUBLE, 16);

struct type *typeadjvalist;

struct type *
mktype(enum typekind kind, enum typeprop prop)
{


@@ 216,6 218,22 @@ typecommonreal(struct type *t1, unsigned w1, struct type *t2, unsigned w2)
	fatal("internal error; could not find common real type");
}

/* function parameter type adjustment (C11 6.7.6.3p7) */
struct type *
typeadjust(struct type *t)
{
	switch (t->kind) {
	case TYPEARRAY:
		t = mkpointertype(t->base, t->qual);
		break;
	case TYPEFUNC:
		t = mkpointertype(t, QUALNONE);
		break;
	}

	return t;
}

struct member *
typemember(struct type *t, const char *name, uint64_t *offset)
{