2

I have a C program that parses a configuration file. The configuration file allows some wildchars in the format option=%any.

The problem is that when I use strcmp to compare the value, I get an illegal instruction error.

Sample program to illustrate this:

char str1[10];
sprintf(str1,"%any");

if(strcmp(str1,"%any") == 0) 
        printf("match\n");

return 0;

Output:

$ ./a.out
Illegal instruction

printf also throws this error.

With printf("%s\n",str1);

output is:

$ ./a.out
0x0.07fff00000001p-1022ny
Illegal instruction

I tried escaping, i.e using "\%any" instead of "%any" in sprintf; but this doesn't help.

In C++, with std::string == comparison, and printing using cout seems to be working fine.

Could some one please help me to find out how to do this with C.

m.divya.mohan
  • 2,261
  • 4
  • 24
  • 34
  • 1
    In every decent documentation of `printf()` is documented the escaping of `%`. Why not just read one of those documentations? –  Nov 08 '12 at 14:43
  • Should not be closed as too localized... asking how to escape `%` is not localized at all. – djechlin Nov 08 '12 at 18:09
  • @djechlin Not reading the documentation makes this too localized (to those who don't read the documentation). –  Nov 08 '12 at 18:20
  • @H2CO3 http://stackoverflow.com/faq#close no, that is a very liberal interpretation, not reading documentation does not constitute an "extremely narrow situation." Downvote as poor research effort per tooltip on downvote error is appropriate. – djechlin Nov 08 '12 at 18:24
  • @djechlin I'm hoping you didn't actually think I haven't yet read the FAQ. –  Nov 08 '12 at 18:25

2 Answers2

6

It comes from sprintf.

#include <stdio.h>

sprintf(str1, "%%any");

As Paul R. stated, you can use rather strcpy to don't worry about these formats.

md5
  • 23,373
  • 3
  • 44
  • 93
  • 2
    Better still, use strcpy rather than sprintf. – Paul R Nov 08 '12 at 14:41
  • 1
    @PaulR Nope, rather `strncpy()` or `snprintf()`. One should really avoid using insecure functions (i. e. those ones which do not take a length argument). –  Nov 08 '12 at 14:43
  • 3
    @H2CO3: I agree with `snprintf`, but not with `strncpy`. This function is not designed to null-terminated strings. – md5 Nov 08 '12 at 14:44
  • 1
    Or, `sprintf(str1, "%s", "%any"). This variant works better when `%any` is not a literal and makes it easy to add something to the string. Of course, it should also be `snprintf`. – user4815162342 Nov 08 '12 at 14:44
  • @Kirilenko And what if your NUL-terminated string is longer than the buffer? I. e. `char buf[10]; strcpy(buf, "dezoxiribonucleic acid");` **will segfault outta hell.** –  Nov 08 '12 at 14:45
  • 2
    @H2CO3 strncpy is NOT a secure version of strcpy. [Read this](http://stackoverflow.com/questions/2114896/why-is-strlcpy-and-strlcat-considered-to-be-insecure). – Lundin Nov 08 '12 at 14:46
  • @Lundin thanks, I already know this, still one must *definitely pay attention* to buffer lengths, and `strcpy()` is *not the way for doing that.* I'd advise you avoid `strcpy()` in production code. –  Nov 08 '12 at 14:48
  • @H2CO3: and what if you use `strncpy` with the same example? The result is not nul-terminated, so you'll probably cause UB eventually anyway. There isn't really any substitute in C for learning to use functions correctly. Calling `strncpy` "secure" and `strcpy` "insecure" discourages any use of `strcpy`, but unfortunately it also discourages correct usage of `strncpy` by suggesting that it is safe/easy to use. There are also some odd situations where `strcpy` is safer than `strncpy`. – Steve Jessop Nov 08 '12 at 14:53
  • Personally I'd advise to *never* use `strncpy` under any circumstances other than the odd use case it was designed for (fixed-width character array fields), use `strcpy` only when you've measured the string length against the buffer size immediately beforehand (e.g. because you've just allocated the buffer) but not necessarily then, and otherwise use something like `strlcpy` or `strcpy_s`, home-grown if necessary. Basically, by all means warn against `strcpy`, but by no means recommend `strncpy` in its place. – Steve Jessop Nov 08 '12 at 15:02
  • @H2CO3 You need to pay attention to the parameters passed to _every_ function in the standard C library. Indeed, strcpy is no exception. – Lundin Nov 08 '12 at 15:11
  • @SteveJessop Okay, I see. Still, this is by no means an appropriate use of `strcpy()` (constant-length buffer with unmeasured string.) –  Nov 08 '12 at 15:11
  • @Lundin And you have to use your brain every time you're writing a program, yet nobody complains about that. The problem is not how much you have to do, it is rather the point that you must try to write as secure code as possible. –  Nov 08 '12 at 15:13
  • @H2CO3: meh, it's a string literal copied in and an integer constant size. It's conceivable that the programmer chose the size wisely, but of course you're right that `strcpy` is less resilient to future changes to one or the other size than it would be to call `snprintf` or `strlcpy` and check the return value. – Steve Jessop Nov 08 '12 at 15:16
  • @SteveJessop of course it's "secure" as long as you're copying the string "foo" to a 10-byte buffer. However, I *assumed* that OP's program does not work with constants only (i. e. that this was an example) so it may be necessary to change the sizes - and in this case, the code wouldn't be secure by any means. –  Nov 08 '12 at 15:24
1

In this case you should probably write one of the following:

char str1[10] = "%any";
char str1[] = "%any"; // str1 has size 5
const char *str1 = "%any"; // str1 points to a string literal

%a in a formatting string is a hex floating point format. Aside from the fact you didn't supply a double argument (causing undefined behavior), this can write more than 10 bytes and hence overrun your buffer str1 (causing undefined behavior).

That's why you see 0x0.07fff00000001p-1022ny, it's a hex floating-point value 0x0.07fff00000001p-1022 followed by ny. Probably the illegal instruction error you get is because you smashed the return address stored on the stack, so when your function returns it jumps to an invalid address.

As a rule of thumb, if you use one of the printf functions and you don't specify at least one vararg after the format string, there's a reasonable chance that you're doing something wrong. The meaning of the f in printf is that the function does formatting, but you're using it when you don't have anything to format. So it's just a string copy with an extra opportunity to get it wrong.

If you want to use one of the printf family of functions to write a string, then you can avoid accidentally specifying a format you didn't mean like this:

sprintf(str1, "%s", SOME_STRING);

or to take advantage of length-checking:

#define STR1_SIZE 10
char str1[STR1_SIZE];

if (snprintf(str1, STR1_SIZE, "%s", SOME_STRING) >= STR1_SIZE) {
    // error-handling here
}

If you somehow know that SOME_STRING fits in your buffer, then you could omit the error-handling and/or use strcpy instead. But you look pretty stupid when you do that and get the lengths wrong.

You can also use sizeof(str1) in place of STR1_SIZE provided that str1 is an array (as it is here) rather than a pointer (as it is in most string-handling code).

I tried escaping, i.e using "\%any" instead of "%any" in sprintf; but this doesn't help.

Nice try, no cigar ;-)

The backslash is to escape special characters in a string literal. Since % doesn't mean anything special in a string literal, escaping it has no effect. The resulting string is five characters %, a, n, y, \0.

Later you pass that string to sprintf. Now the % has special meaning, and has a different way of escaping it: %%.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699