aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2021-09-06 17:08:18 -0700
committerMichael Forney <mforney@mforney.org>2021-09-06 17:08:18 -0700
commit8f9714c3713f4060de4706e099b745d5d4e5ba1f (patch)
tree0e6b019927bfcab6b1d321847e7f1b9cb8495b6e
parent34ae0d48e9669687003c26f94e07fa00d12dd41f (diff)
downloadcproc-8f9714c3713f4060de4706e099b745d5d4e5ba1f.tar.xz
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.
-rw-r--r--cc.h2
-rw-r--r--decl.c19
-rw-r--r--expr.c24
-rw-r--r--targ.c1
-rw-r--r--type.c18
5 files changed, 36 insertions, 28 deletions
diff --git a/cc.h b/cc.h
index c4dbee9..396a4ae 100644
--- a/cc.h
+++ b/cc.h
@@ -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 */
diff --git a/decl.c b/decl.c
index 8b5bfad..de129d5 100644
--- a/decl.c
+++ b/decl.c
@@ -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;
diff --git a/expr.c b/expr.c
index 1c91556..323cbb7 100644
--- a/expr.c
+++ b/expr.c
@@ -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)
diff --git a/targ.c b/targ.c
index f2be127..6e89c51 100644
--- a/targ.c
+++ b/targ.c
@@ -55,4 +55,5 @@ targinit(const char *name)
if (!targ)
fatal("unknown target '%s'", name);
typechar.basic.issigned = targ->signedchar;
+ typeadjvalist = typeadjust(targ->typevalist);
}
diff --git a/type.c b/type.c
index 1d28213..2653310 100644
--- a/type.c
+++ b/type.c
@@ -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)
{