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
+4
View File
@@ -4362,3 +4362,7 @@
happen only under one condition: When the kernel system call logic calls 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 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). 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).
+257 -141
View File
@@ -197,12 +197,12 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
noassign = false; noassign = false;
lflag = false; lflag = false;
/* NOTE that there is a flaw in this loop logic: The fmt string often /* Loop until all characters in the fmt string have been processed. We
* terminates with %n which would have to be processes at the end of the * may have to continue loop after reaching the end the input data in
* buf string. That won't happen here. * order to handle trailing %n format specifiers.
*/ */
while (*fmt && *buf) while (*fmt)
{ {
/* Skip over white space */ /* 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"); lvdbg("vsscanf: Specifier found\n");
/* Check for qualifiers on the conversion specifier */ /* Check for qualifiers on the conversion specifier */
fmt++; fmt++;
for (; *fmt; 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"); lvdbg("vsscanf: Performing string conversion\n");
while (isspace(*buf)) /* 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
buf++; * update the 'ap' variable.
} */
/* Was a fieldwidth specified? */
if (!width)
{
/* No... Guess a field width using some heuristics */
width = findwidth(buf, fmt);
}
tv = NULL; /* To avoid warnings about beign uninitialized */
if (!noassign) if (!noassign)
{ {
tv = va_arg(ap, char*); tv = va_arg(ap, char*);
strncpy(tv, buf, width); tv[0] = '\0';
tv[width] = '\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 */ /* 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"); lvdbg("vsscanf: Performing character conversion\n");
/* Was a fieldwidth specified? */ /* 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
if (!width) * update the 'ap' variable.
{ */
/* No, then width is this one single character */
width = 1;
}
tv = NULL; /* To avoid warnings about beign uninitialized */
if (!noassign) if (!noassign)
{ {
tv = va_arg(ap, char*); tv = va_arg(ap, char*);
strncpy(tv, buf, width); tv[0] = '\0';
tv[width] = '\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 */ /* 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"); lvdbg("vsscanf: Performing integer conversion\n");
/* Skip over any white space before the integer string */ /* 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
while (isspace(*buf)) * update the 'ap' variable.
{
buf++;
}
/* The base of the integer conversion depends on the specific
* conversion specification.
*/ */
if (*fmt == 'd' || *fmt == 'u') FAR long *plong = NULL;
{ FAR int *pint = NULL;
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) if (!noassign)
{ {
#ifdef SDCC /* We have to check whether we need to return a long or an
char *endptr; * int.
long tmplong = strtol(tmp, &endptr, base); */
#else
long tmplong = strtol(tmp, NULL, base);
#endif
if (lflag) if (lflag)
{ {
long *plong = va_arg(ap, long*); plong = va_arg(ap, long*);
lvdbg("vsscanf: Return %ld to 0x%p\n", tmplong, plong); *plong = 0;
*plong = tmplong;
} }
else else
{ {
int *pint = va_arg(ap, int*); pint = va_arg(ap, int*);
lvdbg("vsscanf: Return %ld to 0x%p\n", tmplong, pint); *pint = 0;
*pint = (int)tmplong; }
}
/* 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') 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"); lvdbg("vsscanf: Performing floating point conversion\n");
/* Skip over any white space before the real string */ /* 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
while (isspace(*buf)) * update the 'ap' variable.
{ */
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 */
#ifdef CONFIG_HAVE_DOUBLE
FAR double_t *pd = NULL;
#endif
FAR float *pf = NULL;
if (!noassign) if (!noassign)
{ {
/* strtod always returns a double */ /* We have to check whether we need to return a float or a
#ifdef SDCC * double.
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.
*/ */
#ifdef CONFIG_HAVE_DOUBLE #ifdef CONFIG_HAVE_DOUBLE
if (lflag) if (lflag)
{ {
*((double_t*)pv) = dvalue; pd = va_arg(ap, double_t*);
*pd = 0.0;
} }
else else
#endif #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 #endif
@@ -462,12 +574,12 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap)
if (lflag) if (lflag)
{ {
long *plong = va_arg(ap, long*); FAR long *plong = va_arg(ap, long*);
*plong = (long)nchars; *plong = (long)nchars;
} }
else else
{ {
int *pint = va_arg(ap, int*); FAR int *pint = va_arg(ap, int*);
*pint = (int)nchars; *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 */ /* Its is not a conversion specifier */
else else if (*buf)
{ {
/* Skip over any leading spaces in the input buffer */
while (isspace(*buf)) while (isspace(*buf))
{ {
buf++; buf++;
} }
/* Skip over matching characters in the buffer and format */
if (*fmt != *buf) if (*fmt != *buf)
{ {
break; break;