aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorBen Dooks <ben.dooks@codethink.co.uk>2018-10-26 12:27:02 +0100
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-10-05 12:56:10 +0200
commit375690b61e8ec4735b19bcead756ba526edab57e (patch)
tree3c863d1a181c3446b8f8cbbdf2dbda3702ea64c0
parenta53fa379c76dfd63dc71ecf2f570dad8c624877d (diff)
downloadsparse-375690b61e8ec4735b19bcead756ba526edab57e.tar.gz
parse: initial parsing of __attribute__((format))
Add code to parse the __attribute__((format)) used to indicate that a variadic function takes a printf-style format string and where those are. Save the data in ctype ready for checking when such an function is encountered. Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk> Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
-rw-r--r--parse.c75
-rw-r--r--symbol.h17
-rw-r--r--validation/varargs-format-bad.c1
3 files changed, 89 insertions, 4 deletions
diff --git a/parse.c b/parse.c
index 31ecef0f..55cca01e 100644
--- a/parse.c
+++ b/parse.c
@@ -85,7 +85,7 @@ static attr_t
attribute_address_space, attribute_context,
attribute_designated_init,
attribute_transparent_union, ignore_attribute,
- attribute_mode, attribute_force;
+ attribute_mode, attribute_force, attribute_format;
typedef struct symbol *to_mode_t(struct symbol *);
@@ -377,6 +377,10 @@ static struct symbol_op attr_force_op = {
.attribute = attribute_force,
};
+static struct symbol_op attr_format_op = {
+ .attribute = attribute_format,
+};
+
static struct symbol_op address_space_op = {
.attribute = attribute_address_space,
};
@@ -436,6 +440,16 @@ static struct symbol_op mode_word_op = {
.to_mode = to_word_mode
};
+static struct symbol_op attr_printf_op = {
+ .type = KW_FORMAT,
+ .class = FMT_PRINTF,
+};
+
+static struct symbol_op attr_scanf_op = {
+ .type = KW_FORMAT,
+ .class = FMT_SCANF,
+};
+
/*
* Define the keyword and their effects.
* The entries in the 'typedef' and put in NS_TYPEDEF and
@@ -551,6 +565,9 @@ static struct init_keyword {
D("pure", &attr_fun_op, .mods = MOD_PURE),
A("const", &attr_fun_op, .mods = MOD_PURE),
D("gnu_inline", &attr_fun_op, .mods = MOD_GNU_INLINE),
+ D("format", &attr_format_op),
+ D("printf", &attr_printf_op),
+ D("scanf", &attr_scanf_op),
/* Modes */
D("mode", &mode_op),
@@ -1188,6 +1205,60 @@ static struct token *attribute_address_space(struct token *token, struct symbol
return token;
}
+static int invalid_format_args(long long start, long long at)
+{
+ return start < 0 || at < 0 || start > USHRT_MAX || at > USHRT_MAX ||
+ (start == at && start > 0) ||
+ (start == 0 && at == 0);
+}
+
+static struct token *attribute_format(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+ struct expression *args[3];
+ int type = FMT_UNKNOWN;
+
+ /* expecting format ( type, start, va_args at) */
+
+ token = expect(token, '(', "after format attribute");
+ if (token_type(token) != TOKEN_IDENT) {
+ sparse_error(token->pos, "identifier expected for format type");
+ } else {
+ struct symbol *sym = lookup_keyword(token->ident, NS_KEYWORD);
+ if (sym && sym->op && sym->op->type == KW_FORMAT)
+ type = sym->op->class;
+ }
+ token = conditional_expression(token, &args[0]);
+ token = expect(token, ',', "format attribute type");
+ token = conditional_expression(token, &args[1]);
+ token = expect(token, ',', "format attribute type position");
+ token = conditional_expression(token, &args[2]);
+ token = expect(token, ')', "format attribute arg position");
+
+ if (!args[0] || !args[1] || !args[2]) {
+ // incorrect format attribute
+ } else if (type != FMT_PRINTF) {
+ // only printf-style is supported, skip anything else
+ } else {
+ long long start, at;
+
+ start = get_expression_value(args[2]);
+ at = get_expression_value(args[1]);
+
+ if (invalid_format_args(start, at)) {
+ warning(token->pos, "bad format positions");
+ } else if (start == 0) {
+ /* nothing to do here, is va_list function */
+ } else if (start < at) {
+ warning(token->pos, "format cannot be after va_args");
+ } else {
+ ctx->ctype.format.index = at;
+ ctx->ctype.format.first = start;
+ }
+ }
+
+ return token;
+}
+
static struct symbol *to_QI_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
@@ -2957,6 +3028,8 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
if (!(decl->ctype.modifiers & MOD_STATIC))
decl->ctype.modifiers |= MOD_EXTERN;
+
+ base_type->ctype.format = decl->ctype.format;
} else if (base_type == &void_ctype && !(decl->ctype.modifiers & MOD_EXTERN)) {
sparse_error(token->pos, "void declaration");
}
diff --git a/symbol.h b/symbol.h
index 6d25e7fc..0d5439ee 100644
--- a/symbol.h
+++ b/symbol.h
@@ -82,8 +82,8 @@ enum keyword {
KW_ASM = 1 << 5,
KW_MODE = 1 << 6,
KW_STATIC = 1 << 7,
- // KW UNUSED = 1 << 8,
- KW_EXACT = 1 << 9,
+ KW_EXACT = 1 << 8,
+ KW_FORMAT = 1 << 9,
};
struct context {
@@ -95,12 +95,25 @@ extern struct context *alloc_context(void);
DECLARE_PTR_LIST(context_list, struct context);
+/* the types of formatting from __attribute__((format)) */
+enum {
+ FMT_UNKNOWN,
+ FMT_PRINTF,
+ FMT_SCANF,
+};
+
+struct attr_format {
+ unsigned short index; /* index in argument list for format string */
+ unsigned short first; /* where first variadic argument is */
+};
+
struct ctype {
struct symbol *base_type;
unsigned long modifiers;
unsigned long alignment;
struct context_list *contexts;
struct ident *as;
+ struct attr_format format;
};
struct decl_state {
diff --git a/validation/varargs-format-bad.c b/validation/varargs-format-bad.c
index d4635708..82ae357c 100644
--- a/validation/varargs-format-bad.c
+++ b/validation/varargs-format-bad.c
@@ -9,7 +9,6 @@ static void test(void) {
/*
* check-name: variadic formatting test with bad formatting parameters
* check-command: sparse -Wformat $file
- * check-known-to-fail
*
* check-error-start
varargs-format-bad.c:2:73: warning: bad format positions