Bug 87879 - -Wformat-nonliteral could see more things as literals
Summary: -Wformat-nonliteral could see more things as literals
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 8.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2018-11-05 09:20 UTC by Rasmus Villemoes
Modified: 2018-11-06 04:19 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-11-06 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rasmus Villemoes 2018-11-05 09:20:25 UTC
Consider these examples:

#include <stdio.h>
#include <string.h>
const char *foo (const char *fmt, ...)
  __attribute__((format(printf, 1, 2)));

int f1(const char *s, int x)
{
    const char *fmt = "s: %s, x: %d\n";
    foo(fmt, s, x);
    return strlen(fmt);
}
int f2(const char *s, int x)
{
    const char *fmt = "s: %d, x: %s\n";
    foo(fmt, s, x);
    return strlen(fmt);
}
int g1(const char *s, int x)
{
    const char *const fmt = "ss: %s, xx: %d\n";
    foo(fmt, s, x);
    return strlen(fmt);
}
int g2(const char *s, int x)
{
    const char *const fmt = "ss: %d, xx: %s\n";
    foo(fmt, s, x);
    return strlen(fmt);
}

With -Wformat-nonliteral, gcc diagnoses f1 and f2, which is of course not strictly wrong. However, since fmt is never assigned to apart from its initialization (and its address is not taken and passed on etc.), gcc should be able to deduce the constness of fmt and act as for g1 and g2 (granted, that only works at -O1 and higher). What's worse, -Wformat-nonliteral is not part of -Wall -Wextra, so with "just" -Wall -Wextra, f2() isn't diagnosed at all.

The strlen() calls are just there to show that gcc does fold those to constants in all cases (at -O1 and higher). So this is of course dependent on optimization passes, and the format checking presumably happens quite early.

Neverthess, the f1() case is rather common. Is there some magic one could insert above

  if (TREE_CODE (format_tree) != ADDR_EXPR)
    {
      res->number_non_literal++;
      return;
    }
 
to replace format_tree with its DECL_INITIAL in a case like that, to silence Wformat-nonliteral and allow diagnosing f2()?
Comment 1 jsm-csl@polyomino.org.uk 2018-11-05 18:43:38 UTC
You'd need dataflow information that's not available at that point in the 
front end to know that the initializer is indeed the value of fmt at that 
point in the code.
Comment 2 Martin Sebor 2018-11-06 04:19:59 UTC
Confirmed.  It could be done by integrating -Wformat with -Wformat-overflow.  The latter runs without optimization as well as with it so the integration shouldn't result in too many false negatives.