Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Since we have snprintf, why we don't have a snscanf?

Tags:

I have snprintf and it can avoid a buffer overflow, but why there is no function called snscanf?

Code:

int main()
{
     char * src = "helloeveryone";
     char buf1[5];
     sscanf(src,"%s",buf1); // here is a  array out of bounds

}

So, I think a snscanf is also needed. Why do we have only have snprintf?

like image 279
Lidong Guo Avatar asked Aug 21 '13 22:08

Lidong Guo


People also ask

What does Snprintf return?

The snprintf function returns an integer value that equals, in magnitude, the number of characters written to the area addressed by dest . If the value returned is negative, then either the maxlen character limit was reached or some other error, such as an invalid format specification, has occurred.

What is the use of Snprintf?

The snprintf is a predefined library function of the stdio. h header file, which redirects the output of the standard printf() function to other buffers. The snprint() function is used to format the given strings into a series of characters or values in the buffer area.

Is Snprintf unsafe?

Warning: The sprintf function can be dangerous because it can potentially output more characters than can fit in the allocation size of the string s . Remember that the field width given in a conversion specification is only a minimum value. To avoid this problem, you can use snprintf or asprintf , described below.


3 Answers

The controversial (and optional) Annex K to C11 adds a sscanf_s function which takes an additional argument of type rsize_t (also defined in Annex K) after the pointer argument, specifying the size of the pointed-to array. For better or worse, these functions are not widely supported. You can achieve the same results by putting the size in the conversion specifier, e.g.

char out[20];
sscanf(in, "%19s", out);

but this is awkward and error-prone if the size of the destination object may vary at runtime (you would have to construct the conversion specifier programmatically with snprintf). Note that the field width in the conversion specifier is the maximum number of input characters to read, and sscanf also writes a terminating null byte for %s conversions, so the field width you pass must be strictly less than the size of the destination object.

like image 85
R.. GitHub STOP HELPING ICE Avatar answered Sep 28 '22 04:09

R.. GitHub STOP HELPING ICE


There's no need for an snscanf() because there's no writing to the first buffer argument. The buffer length in snprintf() specifies the size of the buffer where the writing goes to:

char buffer[256];

snprintf(buffer, sizeof(buffer), "%s:%d", s, n);

The buffer in the corresponding position for sscanf() is a null-terminated string; there's no need for an explicit length as you aren't going to write to it (it's a const char * restrict buffer in C99 and C11).

char buffer[256];
char string[100];
int n;
if (sscanf(buffer, "%s %d", string, &n) != 2)
    ...oops...

In the output, you are already expected to specify the length of the strings (though you're probably in the majority if you use %s rather than %99s or whatever is strictly appropriate):

if (sscanf(buffer, "%99s %d", string, &n) != 2)
    ...oops...

It would be nice/useful if you could use %*s as you can with snprintf(), but you can't — in sscanf(), the * means 'do not assign scanned value', not the length. Note that you wouldn't write snscanf(src, sizeof(buf1), "%s", buf1), not least because you can have multiple %s conversion specifications in a single call. Writing snscanf(src, sizeof(buf1), sizeof(buf2), "%s %s", buf1, buf2) makes no sense, not least because it leaves an insoluble problem in parsing the varargs list. It would be nice to have a notation such as snscanf(src, "%@s %@s", sizeof(buf1), buf1, sizeof(buf2), buf2) to obviate the need to specify the field size (minus one) in the format string. Unfortunately, you can't do that with sscanf() et al now.

Annex K of ISO/IEC 9899:2011 (previously TR24731) provides sscanf_s(), which does take lengths for character strings, and which might be used as:

if (sscanf_s(buffer, "%s %d", string, sizeof(string), &n) != 2)
    ...oops...

(Thanks to R.. for reminding me of this theoretical option — theoretically because only Microsoft has implemented the 'safe' functions, and they did not implement them exactly as the standard requires.)

Note that §K.3.3 Common definitions <stddef.h> says: '... The type is rsize_t which is the type size_t.385)' (and footnote 385 says: 'See the description of the RSIZE_MAX macro in <stdint.h>.' That means that in fact you can pass size_t without needing a cast — as long as the value passed is within the range defined by RSIZE_MAX in <stdint.h>. (The general intention is that RSIZE_MAX is a largish number but smaller than SIZE_MAX. For more details, read the 2011 standard, or get TR 24731 from the Open Standards web site.)

like image 29
Jonathan Leffler Avatar answered Sep 28 '22 04:09

Jonathan Leffler


In sscanf(s, format, ...), the the array of characters scanned is a const char *. There is no writing to s. The scanning stops when s[i] is NUL. Little need for an n parameter as an auxiliary limit to the scan.

In sprintf(s, format, ...), the array s is a destination. snprintf(s, n, format, ...) insures that data is not wriiten to s[n] and beyond.


What would be useful is a flag extension to sscanf() conversion specifiers so a limit could easily specified at compile time. (It can be done in a cumbersome fashion today, below, with a dynamic format or with sscanf(src,"%4s",buf1).)

// This is a proposed idea for C. - Not valid code today.
sscanf(src, "%!s", sizeof(buf1), buf)

Here ! would tell sscanf() to read a size_t variable for the size limit the upcoming string. Maybe in C17?


Cumbersome method that works today.

char * src = "helloeveryone";
char buf1[5];
char format[1+20+1+1];
sprintf(format, "%%" "%zu" "s", sizeof(buf1) - 1);
sscanf(src, format, buf1);
like image 35
chux - Reinstate Monica Avatar answered Sep 28 '22 03:09

chux - Reinstate Monica