Bug 39438 - Can't compile a wrapper around strftime with -Werror=format-nonliteral
Summary: Can't compile a wrapper around strftime with -Werror=format-nonliteral
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 4.3.2
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2009-03-11 22:56 UTC by Tobias Mueller
Modified: 2024-01-09 23:46 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2015-02-27 00:00:00


Attachments
minimal C source code that demonstrates the problem (269 bytes, text/plain)
2014-05-12 19:33 UTC, D. Hugh Redelmeier
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Mueller 2009-03-11 22:56:48 UTC
I basically have a wrapper around strftime() and compilation with -Werror=format-nonliteral fails when the wrapper wants to call strftime, because "format not a string literal, format string not checked". 



The code in question looks like this:



#include<time.h>
#include<stdio.h>

#define SIZE 256

size_t my_strftime(char *s, size_t max, const char *fmt,
                   const struct tm *tm)
{
    size_t ret;
    ret = strftime(s, max, fmt, tm);
    return ret;
}

int
main ()
{
    char s[SIZE];
    time_t curtime;
    struct tm* loctime;

    curtime = time(NULL);
    loctime = localtime (&curtime);
    my_strftime(s, SIZE, "Hello %A", loctime);
    printf("%s", s);
    return 0;
} 



muelli@bigbox /tmp $ gcc -v -save-temp -Wformat  -Wformat-nonliteral -Werror=format-nonliteral -o mystrftime{,.c} 
Using built-in specs.
gcc: unrecognized option '-save-temp'
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-cpu=generic --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC) 
COLLECT_GCC_OPTIONS='-v' '-save-temp' '-Wformat' '-Wformat-nonliteral' '-Werror=format-nonliteral' '-o' 'mystrftime' '-mtune=generic'
 /usr/libexec/gcc/x86_64-redhat-linux/4.3.2/cc1 -quiet -v mystrftime.c -quiet -dumpbase mystrftime.c -mtune=generic -auxbase mystrftime -Wformat -Wformat-nonliteral -Werror=format-nonliteral -version -o /tmp/ccznk3Wo.s
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.3.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.3.2/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/x86_64-redhat-linux/4.3.2/include
 /usr/include
End of search list.
GNU C (GCC) version 4.3.2 20081105 (Red Hat 4.3.2-7) (x86_64-redhat-linux)
	compiled by GNU C version 4.3.2 20081105 (Red Hat 4.3.2-7), GMP version 4.2.2, MPFR version 2.3.2.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: c99c7b3dc8e919a4b394102437269a84
mystrftime.c: In function ‘my_strftime’:
mystrftime.c:14: error: format not a string literal, format string not checked
muelli@bigbox /tmp $ 


I raised this issue on gcc-help (Message-ID: <49A96972.5060004@informatik.uni-hamburg.de>) and got the tip to use  __attribute__(( format(strftime, 3, 0) )) but it doesn't work.


I think I want to make gcc
 * know that the wrapper is not responsible for the format string and
   thus the call to strftime is allowed
 * pass the responsibility up to the callers and thus check whether they
   call the wrapper with "good" strings.

but it doesn't seem possible.


I hope to have all needed information included. I can't, however, fulfil the request from http://gcc.gnu.org/bugs.html to attach *.*i* files, because there are none. Also, I searched the bugzilla for "format-nonliteral" and found nothing related to this issue. I thus think it hasn't been filed yet.
Comment 1 Richard Biener 2009-03-12 09:40:08 UTC
It won't work.  You can either put the wrapper in a system header (add
#pragma GCC system_header) or build this TU with -fno-builtin-strftime.
Comment 2 Tobias Mueller 2009-03-12 09:58:16 UTC
Hey Richard. Thanks for the timely feedback.

As far as I know, it works more or less good for printf-wrappers. So I expected this to be working for strftime as well.

I don't think that your proposed solutions are clean and desirable ones, do you?

Is there anything wrong in expecting the posted code snipped to compile fine? Especially if the "fmt" argument is indeed a checked, constant, fixed size (literal) string.
Comment 3 Timo Sirainen 2012-01-10 20:38:44 UTC
I ran into this as well. I tried two ways, neither of which works with strftime (both work with printf):

static void __attribute__((format (strftime, 1, 0)))
test1(const char *fmt, const struct tm *tm)
{
	char buf[100];

	strftime(buf, sizeof(buf), fmt, tm);
}

static const char *__attribute__((format_arg (1)))
helper(const char *fmt)
{
	return fmt;
}

static void
test2(const char *fmt, const struct tm *tm)
{
	char buf[100];

	strftime(buf, sizeof(buf), helper(fmt), tm);
}
Comment 4 D. Hugh Redelmeier 2014-05-10 16:47:09 UTC
I have this problem too.  I'm writing a wrapper for strftime.  I get a warning on the actual strftime call.  

warning: format not a string literal, format string not checked [-Wformat-nonliteral]
  strftime(buf, buflen, fmt, t);

Surely GCC should not that for "fmt" argument has been checked to be a valid strftime format at the points where prettynow gets called.  So there is no need to whine that it is unchecked.


static void prettynow(char *buf, size_t buflen, const char *fmt) __attribute__ ((format (__strftime__, 3, 0)));

static void prettynow(char *buf, size_t buflen, const char *fmt)
{
 	time_t n = time(0);
	struct tm tm1;
	struct tm *t = localtime_r(&n, &tm1);

	strftime(buf, buflen, fmt, t);
}

I'm using gcc-4.8.2-7.fc20.x86_64 on Fedora 20.

I managed to suppress the warning with a very ugly cast:

        ((size_t (*)(char *, size_t, const char *, const struct tm *))strftime)(buf, buflen, fmt, t);
Comment 5 Manuel López-Ibáñez 2014-05-12 12:26:10 UTC
Could you produce a complete testcase that (In reply to D. Hugh Redelmeier from comment #4)
> I have this problem too.  I'm writing a wrapper for strftime.  I get a
> warning on the actual strftime call.  
> 
> warning: format not a string literal, format string not checked
> [-Wformat-nonliteral]
>   strftime(buf, buflen, fmt, t);
> 
> Surely GCC should not that for "fmt" argument has been checked to be a valid
> strftime format at the points where prettynow gets called.  So there is no
> need to whine that it is unchecked.
> 

You should be able to produce a minimal self-contained testcase (the one you posted doesn't compile). See http://gcc.gnu.org/bugs/minimize.html
The best would be to include only the definitions of the types that you need and declare as 'extern' library functions that you use to avoid including headers.

If you can produce another similar example that works with printf, that would also help.

The second step would be to find out why it works with printf and not with strftime, but that would require debugging GCC while compiling your (new) testcases, so saving the first step will make it more likely that someone will do the second.
Comment 6 D. Hugh Redelmeier 2014-05-12 14:58:00 UTC
Responding to Comment 5 by Manuel López-Ibáñez:

Thanks for looking at this.

> Could you produce a complete testcase
===
/* compile with -c -Wformat -Werror=format-nonliteral */
#include <time.h>

extern void prettynow(char *buf, size_t buflen, const char *fmt, struct tm *t) __attribute__ ((format (__strftime__, 3, 0)));

void prettynow(char *buf, size_t buflen, const char *fmt, struct tm *t)
{
        strftime(buf, buflen, fmt, t);
}
===

> The best would be to include only the definitions of the types that you need > and declare as 'extern' library functions that you use to avoid including headers.

I didn't do this part.  My excuses: the type declarations are not portable but the example should be; the solution might be in fixing headers; this way is shorter too.

> If you can produce another similar example that works with printf, that would also help.

I don't think that printf is similar enough.  A strftime format doesn't interact with varargs in a complex and problematic way.  A string, on its own, is or is not a valid strftime format; a string is only a valid printf format when considered with the argument list.

If an arg is marked as a const char * (i.e. unchanging) and has the strftime format attribute, it should be accepted as if it were a literal strftime argument.  After all, the necessary checking would have been done at this routine's points of call.
Comment 7 Manuel López-Ibáñez 2014-05-12 15:38:23 UTC
(In reply to D. Hugh Redelmeier from comment #6)
> > The best would be to include only the definitions of the types that you need > and declare as 'extern' library functions that you use to avoid including headers.
> 
> I didn't do this part.  My excuses: the type declarations are not portable
> but the example should be; the solution might be in fixing headers; this way
> is shorter too.

If you compile with --save-temps (as the instruction say), you will see what GCC parses. All that code makes debugging more complex. So if someone had to debug this, he would have to cut down that code to a few lines. This is one reason why examples should be minimal.
Comment 8 Manuel López-Ibáñez 2014-05-12 15:52:23 UTC
(In reply to D. Hugh Redelmeier from comment #6)
> > If you can produce another similar example that works with printf, that would also help.
> 
> I don't think that printf is similar enough.  A strftime format doesn't
> interact with varargs in a complex and problematic way.  A string, on its
> own, is or is not a valid strftime format; a string is only a valid printf
> format when considered with the argument list.
> 
> If an arg is marked as a const char * (i.e. unchanging) and has the strftime
> format attribute, it should be accepted as if it were a literal strftime
> argument.  After all, the necessary checking would have been done at this
> routine's points of call.

Assuming that the wrapper is marked with the correct attribute, GCC would need to know that this const char* comes from the correct wrapper's argument. I am not sure this is trivial to know at the point of warning without additional work/info in the front-end. Some comments above have suggested that this work/info is done for printf, so the first thing someone looking at this should do is to understand how printf detects this case, and why the same is not done for strftime, and then try to share as much code between the two cases as possible. Perhaps it is a bug, perhaps it was never implemented for strftime. In any case, one would need to debug GCC using two similar and minimal testcases to understand what is going on.

See http://gcc.gnu.org/wiki/DebuggingGCC if you want to see how this works. As an exercise, try to find the point of warning while debugging using your testcase.

Bugs/features are more likely to be fixed if:

1. There is a minimal testcase that reproduces the problem
2. Someone has debugged GCC and identified the problem
3. Someone has done all the legal prerequisites and posted a patch that fixes the problem
4. Someone has tested and submitted the patch for review
5. Someone has taken care of the review comments and committed the patch

We are not even in 1 and the real work is 2 and 3.
Comment 9 D. Hugh Redelmeier 2014-05-12 19:33:48 UTC
Created attachment 32784 [details]
minimal C source code that demonstrates the problem

minimal C source code that demonstrates the problem.
Notice that the (system-provided) declaration annotates the format argument dirrectly with __format.  I haven't found documentation for that.
Comment 10 Manuel López-Ibáñez 2015-02-27 15:41:07 UTC
(In reply to D. Hugh Redelmeier from comment #6)
> If an arg is marked as a const char * (i.e. unchanging) and has the strftime
> format attribute, it should be accepted as if it were a literal strftime
> argument.  After all, the necessary checking would have been done at this
> routine's points of call.

The code that warns is this: gcc/c-family/c-format.c

    if (res.number_non_literal > 0)
      {
        /* Functions taking a va_list normally pass a non-literal format
           string.  These functions typically are declared with
           first_arg_num == 0, so avoid warning in those cases.  */
        if (!(format_types[info->format_type].flags & (int) FMT_FLAG_ARG_CONVERT))
          {
            /* For strftime-like formats, warn for not checking the format
               string; but there are no arguments to check.  */
            warning_at (loc, OPT_Wformat_nonliteral,
                        "format not a string literal, format string not checked");
          }

Thus, there is nothing checking the attributes of the caller. In the same file there is this code:

                tree c;
                for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
                     c;
                     c = TREE_CHAIN (c))
                  if (is_attribute_p ("format", TREE_PURPOSE (c))
                      && (decode_format_type (IDENTIFIER_POINTER
                                              (TREE_VALUE (TREE_VALUE (c))))
                          == info.format_type))
                    break;
                if (c == NULL_TREE)
                  {
                    /* Check if the current function has a parameter to which
                       the format attribute could be attached; if not, it
                       can't be a candidate for a format attribute, despite
                       the vprintf-like or vscanf-like call.  */
                    tree args;
                    for (args = DECL_ARGUMENTS (current_function_decl);
                         args != 0;
                         args = DECL_CHAIN (args))
                      {
                        if (TREE_CODE (TREE_TYPE (args)) == POINTER_TYPE
                            && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (args)))
                                == char_type_node))
                          break;
                      }
                    if (args != 0)
                      warning (OPT_Wsuggest_attribute_format, "function might "
                               "be possible candidate for %qs format attribute",
                               format_types[info.format_type].name);
                  }

I'm confident that the above code can be adjusted and moved to the point of the warning in order to check that the caller of strftime has an attribute format of type 'strftime' that refers to the same declaration as the nonliteral format argument in the call to strftime, then avoid warning. I'm not planning to work on this though.
Comment 11 Alexander Amelkin 2018-08-27 15:54:18 UTC
I'm still seeing this with "gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609".
Is there any chance that this NINE YEAR OLD bug is going to be fixed?
Comment 12 Alexander Amelkin 2018-11-07 15:53:07 UTC
Also happens with `gcc (Ubuntu 8.1.0-5ubuntu1~16.04) 8.1.0`