68

I'm attempting to learn C and already I've run into an issue. I assume its trivial but I need to know it. I have written:

#include <stdio.h>
#include <string.h>

int main() 
{
    char str_a[20];

    strcpy(str_a, "Hello, world!\n");
    printf(str_a);
}

Once I attempt to compile it with: gcc -g -o char_array2 char_array2.c I receive an error saying:

char_array2.c: In function ‘main’:
char_array2.c:9:2: warning: format not a string literal and no format arguments [-Wformat-security]

Can anyone help please?

Jens
  • 69,818
  • 15
  • 125
  • 179
bigl
  • 1,063
  • 3
  • 13
  • 23
  • Possible duplicate of [warning: format not a string literal and no format arguments](http://stackoverflow.com/questions/4419293/warning-format-not-a-string-literal-and-no-format-arguments) – Ciro Santilli OurBigBook.com Dec 10 '15 at 01:05

7 Answers7

107

When using printf, the format string is better as a string literal than a variable:

printf("%s", str_a);
baconcheese113
  • 843
  • 2
  • 13
  • 27
MByD
  • 135,866
  • 28
  • 264
  • 277
  • 3
    Better or required? Why? – Ciro Santilli OurBigBook.com Dec 10 '15 at 01:05
  • 9
    @CiroSantilli六四事件法轮功包卓轩 - not required, as the API itself permits passing variable strings, but much, much better to use a string literal, as using a variable string that happened to have format specifiers will cause bugs (mainly memory corruptions) and from security point of view, this is a clear and simple vulnerability (if an attacker may control the input) – MByD Dec 10 '15 at 07:07
  • 1
    @MByD "will cause bugs (mainly memory corruptions)" I think it's important to point out that this will never occur under nominal circumstances - only when the string is manipulated either by an attacker, or by poor programming. There's nothing causing printf to handle a literal any differently from a `const char *`. – Benjamin Crawford Ctrl-Alt-Tut Oct 03 '19 at 19:58
  • @BenjaminCrawfordCtrl-Alt-Tut - I tend to look at "might" as "will" because I learned the hard way that eventually it will. Poor programming is not "nominal circumstances" but doing exactly this. printf() might not care where the string came from but it is way too powerful to dismiss this precaution. – MByD Oct 10 '19 at 07:48
30

Just to add something to other answers, you better do this because a (long?) time ago people wrote printf like that and hackers found a way to read from and write to the stack, more here.
For example, a simple program like this:

blackbear@blackbear-laptop:~$ cat format_vul.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char text[1024];
    static int test_var = -1;

    if(argc < 2) {
        printf("Use: %s <input>\n", argv[0]);
        exit(-1);
    }

    strcpy(text, argv[1]);

    printf("The correct way:\n");
    printf("%s", text);

    printf("\nThe wrong way:\n");
    printf(text);

    printf("\n[*]: test_var @ %8p = %d ( 0x%x )\n", &test_var, test_var, test_var);
}
blackbear@blackbear-laptop:~$ ./format_vul AAAA
The correct way:
AAAA
The wrong way:
AAAA
[*]: test_var @ 0x804a024 = -1 ( 0xffffffff )

Can be used to change test_var's value from 0xffffff to something else, like 0xaabbccdd:

blackbear@blackbear-laptop:~$ ./format_vul $(printf "\x24\xa0\x04\x08JUNK\x2
5\xa0\x04\x08JUNK\x26\xa0\x04\x08JUNK\x27\xa0\x04\x08").%8x.%8x.%8x.%8x.%8x.
%8x.%8x.%8x.%8x.%110x.%n%239x%n%239x%n%239x%n
The correct way:
$�JUNK%�JUNK&�JUNK'�.%8x.%8x.%8x.%8x.%8x.%8x.%8x.%8x.%8x.%110x.%n%239x%n%239
x%n%239x%n
The wrong way:
$�JUNK%�JUNK&�JUNK'�.bfffefec.  154d7c.  155d7c.  155d7c.      f0.      f0.b
ffff4a4.       4.       4.                                                  
                                                     174.                   


                                                50415243                    


                                               50415243                     


                                              50415243
[*]: test_var @ 0x804a024 = -1430532899 ( 0xaabbccdd )
BlackBear
  • 22,411
  • 10
  • 48
  • 86
7

The warning is caused by the compiler wanting the first argument of printf to be a string literal. It wants you to write this:

printf("%s\n", str_a);

This is because the first parameter of printf is the format string. The format arguments are then passed after that.

Note: You can in fact use a variable as a format string, but you probably shouldn't do that. That's why the compiler issues a warning and not an error.

Marlon
  • 19,924
  • 12
  • 70
  • 101
4

printf() expects it's format to be a string literal, not a dynamically created string. To fix, try this:

printf("%s", str_a); // %s denotes a string

Or use puts

puts(str_a);
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • 1
    It's all the same to `printf()` if you pass it a string literal or a pointer: the first is converted to a pointer before `printf()` even thinks it's going to get called. Also `puts()` adds a `'\n'` to the output and the original string already has one. – pmg Mar 14 '12 at 18:09
  • @pmg it's not the same. With `printf("%s", str)` your argument can have format characters (%c, %i, %f) without risk of undefined behavior with va_arg. – Richard J. Ross III Mar 14 '12 at 18:11
  • 1
    What I mean is that your assertion that `printf()` expects a string literal is false. The compiler, on the other hand, is being helpful by pointing a common mistake in the use of `printf()`. – pmg Mar 14 '12 at 18:24
  • 3
    I've had plenty of cases where I had to build a format string at runtime; warnings like this would have driven me bonkers (and may have created an untenable situation if my code were required to compile with no warnings). There *are* legitimate reasons for using a variable as the format string. – John Bode Mar 14 '12 at 23:02
1

Actually, by writing :

printf(str_a);

You're helping malicious users doing a format string attack. Indeed, they can actually write in the input strings like :

%x%x%x

Without passing the argument after this formats, if your stack is not protected, users with this strings can read in your memory due to the functioning of printf(). I prefer not giving to much details myself, being not an expert about format string attack, but here are some resources that are more detailed !

OWASP Wikipedia

Augustus
  • 11
  • 2
1

Please read the warning 'no format arguments' - i.e. no % in the string.

Try printf("%s", str_a);

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
1

The error is coming from printf(str_a);. Your code should be printf("%s",str_a); take a look at the following link for more info on printf. http://www.cprogramming.com/tutorial/printf-format-strings.html

Jeff Woodard
  • 647
  • 8
  • 15