Fix for trailing %n bug in sscanf (with help from Lorenz Meier)

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5754 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2013-03-18 00:41:42 +00:00
parent 3185d5492b
commit 1a67cb9e1e
2 changed files with 261 additions and 141 deletions

View File

@ -4362,3 +4362,7 @@
happen only under one condition: When the kernel system call logic calls
back into user space in order to allocate user space memory. So it is
expected that the maximum nesting level will be only 2 (2013-03-17).
* libc/stdio/lib_sccanf.c: Correct an error in sscanf. If %n occurs in
the format statement after the input data stream has been fully
parsed, the %n format specifier will not be handled. Reported by
Lorenz Meier (and also earlier by Kate) (2013-03-17).

View File

@ -197,12 +197,12 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
noassign = false;
lflag = false;
/* NOTE that there is a flaw in this loop logic: The fmt string often
* terminates with %n which would have to be processes at the end of the
* buf string. That won't happen here.
/* Loop until all characters in the fmt string have been processed. We
* may have to continue loop after reaching the end the input data in
* order to handle trailing %n format specifiers.
*/
while (*fmt && *buf)
while (*fmt)
{
/* Skip over white space */
@ -218,6 +218,7 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
lvdbg("vsscanf: Specifier found\n");
/* Check for qualifiers on the conversion specifier */
fmt++;
for (; *fmt; fmt++)
{
@ -254,28 +255,50 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
{
lvdbg("vsscanf: Performing string conversion\n");
while (isspace(*buf))
{
buf++;
}
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
/* Get a pointer to the char * value. We need to do this even
* if we have reached the end of the input data in order to
* update the 'ap' variable.
*/
tv = NULL; /* To avoid warnings about beign uninitialized */
if (!noassign)
{
tv = va_arg(ap, char*);
strncpy(tv, buf, width);
tv[width] = '\0';
tv = va_arg(ap, char*);
tv[0] = '\0';
}
buf += width;
/* But we only perform the data conversion is we still have
* bytes remaining in the input data stream.
*/
if (*buf)
{
while (isspace(*buf))
{
buf++;
}
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
/* Copy the string (if we are making an assignment) */
if (!noassign)
{
strncpy(tv, buf, width);
tv[width] = '\0';
}
/* Update the buffer pointer past the string in the input */
buf += width;
}
}
/* Process %c: Character conversion */
@ -284,23 +307,47 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
{
lvdbg("vsscanf: Performing character conversion\n");
/* Was a fieldwidth specified? */
if (!width)
{
/* No, then width is this one single character */
width = 1;
}
/* Get a pointer to the char * value. We need to do this even
* if we have reached the end of the input data in order to
* update the 'ap' variable.
*/
tv = NULL; /* To avoid warnings about beign uninitialized */
if (!noassign)
{
tv = va_arg(ap, char*);
strncpy(tv, buf, width);
tv[width] = '\0';
tv = va_arg(ap, char*);
tv[0] = '\0';
}
buf += width;
/* But we only perform the data conversion is we still have
* bytes remaining in the input data stream.
*/
if (*buf)
{
/* Was a fieldwidth specified? */
if (!width)
{
/* No, then width is this one single character */
width = 1;
}
/* Copy the character(s) (if we are making an assignment) */
if (!noassign)
{
strncpy(tv, buf, width);
tv[width] = '\0';
}
/* Update the buffer pointer past the character(s) in the
* input
*/
buf += width;
}
}
/* Process %d, %o, %b, %x, %u: Various integer conversions */
@ -309,72 +356,110 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
{
lvdbg("vsscanf: Performing integer conversion\n");
/* Skip over any white space before the integer string */
while (isspace(*buf))
{
buf++;
}
/* The base of the integer conversion depends on the specific
* conversion specification.
/* Get a pointer to the integer value. We need to do this even
* if we have reached the end of the input data in order to
* update the 'ap' variable.
*/
if (*fmt == 'd' || *fmt == 'u')
{
base = 10;
}
else if (*fmt == 'x')
{
base = 16;
}
else if (*fmt == 'o')
{
base = 8;
}
else if (*fmt == 'b')
{
base = 2;
}
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
/* Copy the numeric string into a temporary working buffer. */
strncpy(tmp, buf, width);
tmp[width] = '\0';
lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp);
/* Perform the integer conversion */
buf += width;
FAR long *plong = NULL;
FAR int *pint = NULL;
if (!noassign)
{
#ifdef SDCC
char *endptr;
long tmplong = strtol(tmp, &endptr, base);
#else
long tmplong = strtol(tmp, NULL, base);
#endif
/* We have to check whether we need to return a long or an
* int.
*/
if (lflag)
{
long *plong = va_arg(ap, long*);
lvdbg("vsscanf: Return %ld to 0x%p\n", tmplong, plong);
*plong = tmplong;
plong = va_arg(ap, long*);
*plong = 0;
}
else
{
int *pint = va_arg(ap, int*);
lvdbg("vsscanf: Return %ld to 0x%p\n", tmplong, pint);
*pint = (int)tmplong;
pint = va_arg(ap, int*);
*pint = 0;
}
}
/* But we only perform the data conversion is we still have
* bytes remaining in the input data stream.
*/
if (*buf)
{
/* Skip over any white space before the integer string */
while (isspace(*buf))
{
buf++;
}
/* The base of the integer conversion depends on the
* specific conversion specification.
*/
if (*fmt == 'd' || *fmt == 'u')
{
base = 10;
}
else if (*fmt == 'x')
{
base = 16;
}
else if (*fmt == 'o')
{
base = 8;
}
else if (*fmt == 'b')
{
base = 2;
}
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
/* Copy the numeric string into a temporary working
* buffer.
*/
strncpy(tmp, buf, width);
tmp[width] = '\0';
lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp);
/* Perform the integer conversion */
buf += width;
if (!noassign)
{
#ifdef SDCC
char *endptr;
long tmplong = strtol(tmp, &endptr, base);
#else
long tmplong = strtol(tmp, NULL, base);
#endif
/* We have to check whether we need to return a long
* or an int.
*/
if (lflag)
{
lvdbg("vsscanf: Return %ld to 0x%p\n",
tmplong, plong);
*plong = tmplong;
}
else
{
lvdbg("vsscanf: Return %ld to 0x%p\n",
tmplong, pint);
*pint = (int)tmplong;
}
}
}
}
@ -383,68 +468,95 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
else if (*fmt == 'f')
{
#ifndef CONFIG_LIBC_FLOATINGPOINT
/* No floating point conversions */
void *pv = va_arg(ap, void*);
lvdbg("vsscanf: Return 0.0 to %p\n", pv);
*((double_t*)pv) = 0.0;
#else
lvdbg("vsscanf: Performing floating point conversion\n");
/* Skip over any white space before the real string */
while (isspace(*buf))
{
buf++;
}
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
/* Copy the real string into a temporary working buffer. */
strncpy(tmp, buf, width);
tmp[width] = '\0';
buf += width;
lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp);
/* Perform the floating point conversion */
/* Get a pointer to the double value. We need to do this even
* if we have reached the end of the input data in order to
* update the 'ap' variable.
*/
#ifdef CONFIG_HAVE_DOUBLE
FAR double_t *pd = NULL;
#endif
FAR float *pf = NULL;
if (!noassign)
{
/* strtod always returns a double */
#ifdef SDCC
char *endptr;
double_t dvalue = strtod(tmp,&endptr);
#else
double_t dvalue = strtod(tmp, NULL);
#endif
void *pv = va_arg(ap, void*);
lvdbg("vsscanf: Return %f to %p\n", dvalue, pv);
/* But we have to check whether we need to return a
* float or a double.
/* We have to check whether we need to return a float or a
* double.
*/
#ifdef CONFIG_HAVE_DOUBLE
if (lflag)
{
*((double_t*)pv) = dvalue;
pd = va_arg(ap, double_t*);
*pd = 0.0;
}
else
#endif
{
*((float*)pv) = (float)dvalue;
pf = va_arg(ap, float*);
*pf = 0.0;
}
}
#ifdef CONFIG_LIBC_FLOATINGPOINT
/* But we only perform the data conversion is we still have
* bytes remaining in the input data stream.
*/
if (*buf)
{
/* Skip over any white space before the real string */
while (isspace(*buf))
{
buf++;
}
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
/* Copy the real string into a temporary working buffer. */
strncpy(tmp, buf, width);
tmp[width] = '\0';
buf += width;
lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp);
/* Perform the floating point conversion */
if (!noassign)
{
/* strtod always returns a double */
#ifdef SDCC
FAR char *endptr;
double_t dvalue = strtod(tmp,&endptr);
#else
double_t dvalue = strtod(tmp, NULL);
#endif
/* We have to check whether we need to return a float
* or a double.
*/
#ifdef CONFIG_HAVE_DOUBLE
if (lflag)
{
lvdbg("vsscanf: Return %f to %p\n", dvalue, pd);
*pd = dvalue;
}
else
#endif
{
lvdbg("vsscanf: Return %f to %p\n", dvalue, pf);
*pf = (float)dvalue;
}
}
}
#endif
@ -462,12 +574,12 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
if (lflag)
{
long *plong = va_arg(ap, long*);
FAR long *plong = va_arg(ap, long*);
*plong = (long)nchars;
}
else
{
int *pint = va_arg(ap, int*);
FAR int *pint = va_arg(ap, int*);
*pint = (int)nchars;
}
}
@ -489,13 +601,17 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
/* Its is not a conversion specifier */
else
else if (*buf)
{
/* Skip over any leading spaces in the input buffer */
while (isspace(*buf))
{
buf++;
}
/* Skip over matching characters in the buffer and format */
if (*fmt != *buf)
{
break;