Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NULL arg allowed to sscanf?

Tags:

c

scanf

Is a NULL pointer allowed as the string to store result in in a call to sscanf? I don't find anything about it in any documentation but it seems to be working fine. Same thing with scanf.

Example:

int main(int arc, char* argv[])
{
  char* s = NULL;
  sscanf("Privjet mir!", "%s", s);
  printf("s: %s\n", s);
  return 0;
}

Output: s: (null)

like image 453
Lii Avatar asked Mar 02 '11 14:03

Lii


People also ask

Does Sscanf null terminate?

No terminating null is added. Pointer to char large enough for input field. Like c , a sequence of bytes of type char (signed or unsigned), except that white space characters are not allowed, and a terminating null is always added.

Does Sscanf move pointer?

You are correct: sscanf indeed does not "move", because there is nothing to move. If you need to scan a bunch of ints, you can use strtol - it tells you how much it read, so you can feed the next pointer back to the function on the next iteration.

What does %n do in Sscanf?

When we use the %n specifier in scanf() it will assign the number of characters read by the scanf() function until it occurs. Key points: It is an edit conversion code.

Does Sscanf call strlen?

each of the sscanfs will call strlen once, but each strlen will scan the full remaining huge string to find the \0 eos byte. the sum of all scanned bytes is quadratic to the length of the huge input string.


2 Answers

No:

Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null character ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.

(http://linux.die.net/man/3/sscanf)

like image 184
Lightness Races in Orbit Avatar answered Oct 10 '22 00:10

Lightness Races in Orbit


As is mentioned by the other answers NULL is not valid to pass to sscanf as an additional argument.

http://www.cplusplus.com/reference/cstdio/sscanf says of additional arguments:

Depending on the format string, the function may expect a sequence of additional arguments, each containing a pointer to allocated storage where the interpretation of the extracted characters is stored with the appropriate type.

For the %s specifier these extracted characters are:

Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence.

So when the "non-whitespace characters" and "terminating null character" is stored, there will be a segfault. Which is exactly what Visual Studio will yield (you can test that this fails at http://webcompiler.cloudapp.net/):

enter image description here

Now as far as non-Visual Studio compilers, libc's extraction code for the %s specifier: https://github.com/ffainelli/uClibc/blob/master/libc/stdio/_scanf.c#L1376 has the leading comment: /* We might have to handle the allocation ourselves */ this is because:

The GNU C library supported the dynamic allocation conversion specifier (as a nonstandard extension) via the a character. This feature seems to be present at least as far back as glibc 2.0.
Since version 2.7, glibc also provides the m modifier for the same purpose as the a modifier.

[Source]

So because libc extracts to a buffer constructed internally to sscanf and subsequently checks that the buffer parameter has no flags set before assigning it, it will never write characters to a NULL buffer parameter.

I can't stress enough that this is non-standard, and is not guaranteed to be preserved even between minor library updates. A far better way to do this is to use the * sub-specifier which:

Indicates that the data is to be read from the stream but ignored (i.e. it is not stored in the location pointed by an argument).

[Source]

This could be accomplished like this for example:

s == NULL ? sscanf("Privjet mir!", "%*s") : sscanf("Privjet mir!", "%s", s);

Obviously the true-branch of the ternary is a no-op, but I've included it with the expectation that other data was expected to be read from the string.

like image 39
Jonathan Mee Avatar answered Oct 10 '22 02:10

Jonathan Mee