From patchwork Wed Apr 3 15:35:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884027 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5FBA4922 for ; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 48A4928770 for ; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3D4B4287F4; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AE61128770 for ; Wed, 3 Apr 2019 15:35:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725959AbfDCPf6 (ORCPT ); Wed, 3 Apr 2019 11:35:58 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51727 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726074AbfDCPf6 (ORCPT ); Wed, 3 Apr 2019 11:35:58 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VN-K5; Wed, 03 Apr 2019 16:35:54 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhvd-00067d-VG; Wed, 03 Apr 2019 16:35:53 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 2/6] parse: initial parsing of __attribute__((format)) Date: Wed, 3 Apr 2019 16:35:48 +0100 Message-Id: <20190403153552.23461-3-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- Fixes since v1: - moved to using ctype in base_type to store infromation - fixed formatting issues - updated check for bad format arguments - reduced the line count to unsigned short to save space Fixes since v2: - correctly use the decl->ctype to store printf information - fixed issues with checking format positions for va_list code - parse but ignore scanf formatting for now Fixes since v4: - deal with function pointers losing format info --- parse.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- symbol.h | 2 ++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/parse.c b/parse.c index f291e24..42ba457 100644 --- a/parse.c +++ b/parse.c @@ -87,7 +87,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 *); @@ -136,6 +136,10 @@ static void asm_modifier_inline(struct token *token, unsigned long *mods) asm_modifier(token, mods, MOD_INLINE); } +enum { + FmtPrintf = 0, FmtScanf, +}; + static struct symbol_op typedef_op = { .type = KW_MODIFIER, .declarator = typedef_specifier, @@ -386,6 +390,10 @@ static struct symbol_op attr_force_op = { .attribute = attribute_force, }; +static struct symbol_op attr_format = { + .attribute = attribute_format, +}; + static struct symbol_op address_space_op = { .attribute = attribute_address_space, }; @@ -445,6 +453,16 @@ static struct symbol_op mode_word_op = { .to_mode = to_word_mode }; +static struct symbol_op attr_printf_op = { + .type = KW_FORMAT, + .class = FmtPrintf, +}; + +static struct symbol_op attr_scanf_op = { + .type = KW_FORMAT, + .class = FmtScanf, +}; + /* Using NS_TYPEDEF will also make the keyword a reserved one */ static struct init_keyword { const char *name; @@ -570,6 +588,10 @@ static struct init_keyword { {"externally_visible", NS_KEYWORD, .op = &ext_visible_op }, {"__externally_visible__", NS_KEYWORD, .op = &ext_visible_op }, + { "format", NS_KEYWORD, .op = &attr_format }, + { "printf", NS_KEYWORD, .op = &attr_printf_op }, + { "scanf", NS_KEYWORD, .op = &attr_scanf_op }, + { "mode", NS_KEYWORD, .op = &mode_op }, { "__mode__", NS_KEYWORD, .op = &mode_op }, { "QI", NS_KEYWORD, .op = &mode_QI_op }, @@ -1172,6 +1194,74 @@ static struct token *attribute_address_space(struct token *token, struct symbol return token; } +static int invalid_printf_format_args(long long start, long long at) +{ + return start < 0 || at < 0 || (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; + int argc = 0; + + /* expecting format ( type, start, va_args at) */ + + token = expect(token, '(', "after format attribute"); + while (!match_op(token, ')')) { + struct expression *expr = NULL; + + if (argc == 0) { + if (token_type(token) == TOKEN_IDENT) + fmt_sym = lookup_keyword(token->ident, NS_KEYWORD); + + if (!fmt_sym || !fmt_sym->op || + fmt_sym->op->type != KW_FORMAT) { + sparse_error(token->pos, + "unknown format type '%s'\n", + show_ident(token->ident)); + fmt_sym = NULL; + } + } + + token = conditional_expression(token, &expr); + if (!expr) + break; + if (argc < 3) + args[argc++] = expr; + if (!match_op(token, ',')) + break; + token = token->next; + } + + if (argc != 3 || !fmt_sym) { + warning(token->pos, "incorrect format attribute"); + } else if (fmt_sym->op->class != FmtPrintf) { + /* 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_printf_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.printf_va_start = start; + ctx->ctype.printf_msg = at; + } + } + + token = expect(token, ')', "after format attribute"); + return token; +} + static struct symbol *to_QI_mode(struct symbol *ctype) { if (ctype->ctype.base_type != &int_type) @@ -2981,8 +3071,18 @@ 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.printf_msg = decl->ctype.printf_msg; + base_type->ctype.printf_va_start = decl->ctype.printf_va_start; } else if (base_type == &void_ctype && !(decl->ctype.modifiers & MOD_EXTERN)) { sparse_error(token->pos, "void declaration"); + } else if (base_type && base_type->type == SYM_PTR) { + // think this is correct to do // + struct symbol *ptr_base = get_base_type(base_type); + if (ptr_base) { + ptr_base->ctype.printf_msg = decl->ctype.printf_msg; + ptr_base->ctype.printf_va_start = decl->ctype.printf_va_start; + } } if (base_type == &incomplete_ctype) { warning(decl->pos, "'%s' has implicit type", show_ident(decl->ident)); diff --git a/symbol.h b/symbol.h index ac43b31..7bb6f29 100644 --- a/symbol.h +++ b/symbol.h @@ -86,6 +86,7 @@ enum keyword { KW_SHORT = 1 << 7, KW_LONG = 1 << 8, KW_EXACT = 1 << 9, + KW_FORMAT = 1 << 10, }; struct context { @@ -103,6 +104,7 @@ struct ctype { struct context_list *contexts; struct ident *as; struct symbol *base_type; + unsigned short printf_va_start, printf_msg; }; struct decl_state {