php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #64874
Patch JSONFix_with_test_fixed.patch revision 2013-09-10 22:07 UTC by ajf at ajf dot me
Patch JSONFix_with_test.patch revision 2013-09-10 21:38 UTC by ajf at ajf dot me
Patch JSONFix.patch revision 2013-09-10 21:23 UTC by ajf at ajf dot me

Patch JSONFix_with_test_fixed.patch for JSON related Bug #64874

Patch version 2013-09-10 22:07 UTC

Return to Bug #64874 | Download this patch
This patch renders other patches obsolete

Obsolete patches:

Patch Revisions:

Developer: ajf@ajf.me

From 8681540e1ac203ca1260201dfe4ab0e101e6ae90 Mon Sep 17 00:00:00 2001
From: Andrea Faulds <ajf@ajf.me>
Date: Tue, 10 Sep 2013 21:22:06 +0000
Subject: [PATCH] Fixed bug #64874 (json_decode handles whitespace and
 case-sensitivity incorrectly)

---
 ext/json/json.c              |   30 +++++++++++-----
 ext/json/tests/bug64874.phpt |   77 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+), 8 deletions(-)
 create mode 100644 ext/json/tests/bug64874.phpt

diff --git a/ext/json/json.c b/ext/json/json.c
index 8c8963d..93fd6bf 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -684,21 +684,35 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
 		double d;
 		int type, overflow_info;
 		long p;
+		char *trim = str;
+		int trim_len = str_len;
+
+		/* Increment trimmed string pointer to strip leading whitespace */
+		/* JSON RFC says to consider as whitespace: space, tab, LF or CR */
+		while (trim_len && (*trim == ' ' || *trim == '\t' || *trim == '\n' || *trim == '\r')) {
+			trim++;
+			trim_len--;
+		}
+
+		/* Decrement trimmed string length to strip trailing whitespace */
+		while (trim_len && (trim[trim_len - 1] == ' ' || trim[trim_len - 1] == '\t' || trim[trim_len - 1] == '\n' || trim[trim_len - 1] == '\r')) {
+			trim_len--;
+		}
 
 		RETVAL_NULL();
-		if (str_len == 4) {
-			if (!strcasecmp(str, "null")) {
+		if (trim_len == 4) {
+			if (!strncmp(trim, "null", trim_len)) {
 				/* We need to explicitly clear the error because its an actual NULL and not an error */
 				jp->error_code = PHP_JSON_ERROR_NONE;
 				RETVAL_NULL();
-			} else if (!strcasecmp(str, "true")) {
+			} else if (!strncmp(trim, "true", trim_len)) {
 				RETVAL_BOOL(1);
 			}
-		} else if (str_len == 5 && !strcasecmp(str, "false")) {
+		} else if (trim_len == 5 && !strncmp(trim, "false", trim_len)) {
 			RETVAL_BOOL(0);
 		}
 
-		if ((type = is_numeric_string_ex(str, str_len, &p, &d, 0, &overflow_info)) != 0) {
+		if ((type = is_numeric_string_ex(trim, trim_len, &p, &d, 0, &overflow_info)) != 0) {
 			if (type == IS_LONG) {
 				RETVAL_LONG(p);
 			} else if (type == IS_DOUBLE) {
@@ -711,10 +725,10 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
 					int i;
 					zend_bool is_float = 0;
 
-					for (i = (str[0] == '-' ? 1 : 0); i < str_len; i++) {
+					for (i = (trim[0] == '-' ? 1 : 0); i < trim_len; i++) {
 						/* Not using isdigit() because it's locale specific,
 						 * but we expect JSON input to always be UTF-8. */
-						if (str[i] < '0' || str[i] > '9') {
+						if (trim[i] < '0' || trim[i] > '9') {
 							is_float = 1;
 							break;
 						}
@@ -723,7 +737,7 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
 					if (is_float) {
 						RETVAL_DOUBLE(d);
 					} else {
-						RETVAL_STRINGL(str, str_len, 1);
+						RETVAL_STRINGL(trim, trim_len, 1);
 					}
 				} else {
 					RETVAL_DOUBLE(d);
diff --git a/ext/json/tests/bug64874.phpt b/ext/json/tests/bug64874.phpt
new file mode 100644
index 0000000..10c99f8
--- /dev/null
+++ b/ext/json/tests/bug64874.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Bug #64874 (json_decode does not properly decode with options parameter)
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+function decode($json) {
+    var_dump(json_decode($json));
+    var_dump(json_last_error() !== 0);
+    echo "\n";
+}
+
+// Only lowercase should work
+decode('true');
+decode('True');
+decode('false');
+decode('False');
+decode('null');
+decode('Null');
+
+// Leading whitespace should be ignored
+decode(" true");
+decode("\ttrue");
+decode("\ntrue");
+decode("\rtrue");
+
+// So should trailing whitespace
+decode("true ");
+decode("true\t");
+decode("true\n");
+decode("true\r");
+
+echo "Done\n";
+--EXPECT--
+bool(true)
+bool(false)
+
+NULL
+bool(true)
+
+bool(false)
+bool(false)
+
+NULL
+bool(true)
+
+NULL
+bool(false)
+
+NULL
+bool(true)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+bool(true)
+bool(false)
+
+Done
-- 
1.7.10.4

 
PHP Copyright © 2001-2015 The PHP Group
All rights reserved.
Last updated: Fri May 29 02:01:51 2015 UTC