standards/173421: [patch] strptime() accepts formats that should be rejected

Fabian Keil fk at fabiankeil.de
Tue Nov 6 14:30:01 UTC 2012


>Number:         173421
>Category:       standards
>Synopsis:       [patch] strptime() accepts formats that should be rejected
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-standards
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Nov 06 14:30:00 UTC 2012
>Closed-Date:
>Last-Modified:
>Originator:     Fabian Keil
>Release:        HEAD
>Organization:
>Environment:
FreeBSD r500.local 10.0-CURRENT FreeBSD 10.0-CURRENT #507 r+e8d2611: Mon Nov  5 13:00:26 CET 2012     fk at r500.local:/usr/obj/usr/src/sys/ZOEY  amd64

>Description:
If two digits are followed by a space in buf, %H lets strptime()
parse the two digits as hours which is correct, but then move the
format pointer to the end, thus ignoring the rest of buf and
reporting a successful parse operation without looking at any
additional format specifiers.
    
This lets the format "%a, %d-%b-%y %H:%M:%S" "match"
"Thursday, 18-Oct-2012 00:11:22", even though the parsing should
fail after %H matched the 12 (because %y only matches the 20).

The resulting time is obviously incorrect.

This affects applications that deal with multiple date variations
by testing several different formats until finding one that is
accepted by strptime(), for example applications that parse dates
in HTTP headers. I noticed the problem while working on Privoxy.

I only confirmed this problem with the %H specifier, but from the
code it looks like other format specifiers are affected as well.

Applications can work around the problem by checking
"%a, %d-%b-%Y %H:%M:%S" before "%a, %d-%b-%y %H:%M:%S",
but this triggers bugs in other strptime() implementations.

The attached patch solves the problem by completely removing the
code that causes the format pointer skipping. This may be incorrect,
but I couldn't think of a scenario where the code is useful, only of
scenarios where it doesn't hurt.

My test case is also handled correctly if the second while()
condition in the removed code is reversed, which might look
reasonable on a first glance, but breaks if %H is followed
by a space and an additional format specifier, in which case
the %H case would move the format pointer to the beginning of
the next format specifier, which would then reject the space
in the buf.

The second patch additionally removes the questionable code for
a couple of other format specifiers, but at least for my use case
the code didn't cause any problems as the skip condition is always
false. Again I couldn't think of a scenario where it's useful,
though.

>How-To-Repeat:
The output of the test case (which I'll submit per mail to keep
the formatting) should be:

Supposedly matching format: %a, %d-%b-%Y %H:%M:%S
Thursday, 18-Oct-2012 00:11:22 GMT -> Thursday, 18-Oct-2012 00:11:22 GMT
ok

Without the patch it's:

Supposedly matching format: %a, %d-%b-%y %H:%M:%S
Thursday, 18-Oct-2012 00:11:22 GMT -> Sunday, 18-Oct-2020 12:00:00 GMT
fail
>Fix:
Apply the attached patch after confirming that the removed code indeed does nothing useful.

Patch attached with submission follows:

>From 6c81c0d84f333075569e84ef9008760bd4689e19 Mon Sep 17 00:00:00 2001
From: Fabian Keil <fk at fabiankeil.de>
Date: Thu, 18 Oct 2012 18:23:00 +0200
Subject: [PATCH 1/2] Fix hour parsing with %H (or break it differently?)

If two digits were followed by a space in buf, %H would parse
the two digits and move the format pointer to the end, thus
reporting a completely successful parse operation without
looking at any additional format specifiers.

This would let the format "%a, %d-%b-%y %H:%M:%S" match
"Thursday, 18-Oct-2012 00:11:22", even though the parsing should
fail after %H matched the 12 (because %y only matches the 20).

---
 lib/libc/stdtime/strptime.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c
index a4ea3fd..3117d3b 100644
--- a/lib/libc/stdtime/strptime.c
+++ b/lib/libc/stdtime/strptime.c
@@ -285,11 +285,6 @@ label:
 
 			tm->tm_hour = i;
 
-			if (*buf != 0 &&
-			    isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 'p':
-- 
1.7.12.4


>From 7b008a9c6555976d5531b45bc72999c27be7e33e Mon Sep 17 00:00:00 2001
From: Fabian Keil <fk at fabiankeil.de>
Date: Fri, 26 Oct 2012 11:45:02 +0200
Subject: [PATCH 2/2] strptime: Apply the previous fix to the rest of the
 format specifiers

Not thoroughly tested.
---
 lib/libc/stdtime/strptime.c | 30 ------------------------------
 1 file changed, 30 deletions(-)

diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c
index 3117d3b..7a03c96 100644
--- a/lib/libc/stdtime/strptime.c
+++ b/lib/libc/stdtime/strptime.c
@@ -248,11 +248,6 @@ label:
 				tm->tm_sec = i;
 			}
 
-			if (*buf != 0 &&
-				isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 'H':
@@ -354,11 +349,6 @@ label:
 			if (i > 53)
 				return 0;
 
-			if (*buf != 0 &&
-			    isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 'w':
@@ -371,11 +361,6 @@ label:
 
 			tm->tm_wday = i;
 
-			if (*buf != 0 &&
-			    isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 'd':
@@ -403,11 +388,6 @@ label:
 
 			tm->tm_mday = i;
 
-			if (*buf != 0 &&
-			    isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 'B':
@@ -464,11 +444,6 @@ label:
 
 			tm->tm_mon = i - 1;
 
-			if (*buf != 0 &&
-			    isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 's':
@@ -517,11 +492,6 @@ label:
 
 			tm->tm_year = i;
 
-			if (*buf != 0 &&
-			    isspace_l((unsigned char)*buf, locale))
-				while (*ptr != 0 &&
-				       !isspace_l((unsigned char)*ptr, locale))
-					ptr++;
 			break;
 
 		case 'Z':
-- 
1.7.12.4



>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-standards mailing list