From: Ben Dooks <ben.dooks@codethink.co.uk>
To: linux-sparse@vger.kernel.org,
Luc Van Oostenryck <luc.vanoostenryck@gmail.com>,
Chris Li <sparse@chrisli.org>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Subject: [PATCH RESEND2 1/4] parse: initial parsing of __attribute__((format))
Date: Mon, 20 Oct 2025 16:39:15 +0100 [thread overview]
Message-ID: <20251020153918.812235-2-ben.dooks@codethink.co.uk> (raw)
In-Reply-To: <20251020153918.812235-1-ben.dooks@codethink.co.uk>
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 encoutered.
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
parse.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
symbol.h | 9 ++++++-
2 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/parse.c b/parse.c
index 3f67451e..af4e5b50 100644
--- a/parse.c
+++ b/parse.c
@@ -86,7 +86,7 @@ static attr_t
attribute_cleanup,
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 *);
@@ -121,6 +121,12 @@ static void asm_modifier(struct token *token, unsigned long *mods, unsigned long
*mods |= mod;
}
+/* the types of formatting from __attribute__((format)) */
+enum {
+ FMT_PRINTF = 0,
+ FMT_SCANF,
+};
+
static struct symbol_op typedef_op = {
.type = KW_MODIFIER,
.declarator = storage_specifier,
@@ -382,6 +388,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,
};
@@ -441,6 +451,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
@@ -557,6 +577,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),
@@ -1217,6 +1240,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];
+ struct symbol *fmt_sym = NULL;
+
+ /* expecting format ( type, start, va_args at) */
+
+ token = expect(token, '(', "after format attribute");
+ if (token_type(token) == TOKEN_IDENT)
+ fmt_sym = lookup_keyword(token->ident, NS_KEYWORD);
+ if (fmt_sym)
+ if (!fmt_sym->op || fmt_sym->op->type != KW_FORMAT)
+ fmt_sym = NULL;
+
+ 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 (!fmt_sym || !args[0] || !args[1] || !args[2]) {
+ warning(token->pos, "incorrect format attribute");
+ } else if (fmt_sym->op->class != FMT_PRINTF) {
+ /* skip anything that isn't printf for the moment */
+ warning(token->pos, "only printf format attribute supported");
+ } 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)
@@ -3007,6 +3084,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 88130c15..0ea46da8 100644
--- a/symbol.h
+++ b/symbol.h
@@ -82,8 +82,9 @@ enum keyword {
KW_ASM = 1 << 5,
KW_MODE = 1 << 6,
KW_STATIC = 1 << 7,
- // KW UNUSED = 1 << 8,
+ // KW_UNUSED = 1 << 8.
KW_EXACT = 1 << 9,
+ KW_FORMAT = 1 << 10,
};
struct context {
@@ -95,12 +96,18 @@ extern struct context *alloc_context(void);
DECLARE_PTR_LIST(context_list, struct context);
+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 {
--
2.37.2.352.g3c44437643
next prev parent reply other threads:[~2025-10-20 15:39 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-20 15:39 Add printf/scanf -Wformat checking Ben Dooks
2025-10-20 15:39 ` Ben Dooks [this message]
2025-10-20 15:39 ` [PATCH RESEND2 2/4] add -Wformat Ben Dooks
2025-10-20 15:39 ` [PATCH RESEND2 3/4] evaluate: check variadic argument types against formatting info Ben Dooks
2025-10-20 15:39 ` [PATCH RESEND2 4/4] tests: add varargs printf format tests Ben Dooks
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251020153918.812235-2-ben.dooks@codethink.co.uk \
--to=ben.dooks@codethink.co.uk \
--cc=linux-sparse@vger.kernel.org \
--cc=luc.vanoostenryck@gmail.com \
--cc=sparse@chrisli.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).