[Pkg-bitcoin-commits] [libunivalue] 17/38: Don't require nul-terminated string inputs
Jonas Smedegaard
dr at jones.dk
Mon Feb 27 01:13:27 UTC 2017
This is an automated email from the git hooks/post-receive script.
js pushed a commit to branch master
in repository libunivalue.
commit fd32d1ab85c82356f5e8a848514a60e65b03cfe5
Author: Russell Yanofsky <russ at yanofsky.org>
Date: Tue Nov 1 15:49:43 2016 -0400
Don't require nul-terminated string inputs
Avoid giving '\0' characters special treatment in JSON parsing code. This way
the parser will reject input strings like "[1,2,3]\0-extra-nonsense" instead of
returning a partially parsed interpretation of the input. It also allows the
parser to be called with char pointers that might not be nul terminated (like
the char pointers returned by std::string::data()).
Fixes following test failures in https://github.com/nst/JSONTestSuite:
bitcoin SHOULD_HAVE_FAILED n_multidigit_number_then_00.json
---
Makefile.am | 8 +++++++-
include/univalue.h | 5 +++--
lib/univalue.cpp | 2 +-
lib/univalue_read.cpp | 46 +++++++++++++++++++++++++++-------------------
test/.gitignore | 1 +
test/fail42.json | Bin 0 -> 37 bytes
test/no_nul.cpp | 8 ++++++++
test/unitester.cpp | 1 +
8 files changed, 48 insertions(+), 23 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 6c1ec81..ee3b33e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,7 +20,7 @@ libunivalue_la_LDFLAGS = \
-no-undefined
libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
-TESTS = test/unitester
+TESTS = test/unitester test/no_nul
GENBIN = gen/gen$(BUILD_EXEEXT)
GEN_SRCS = gen/gen.cpp
@@ -42,6 +42,11 @@ test_unitester_LDADD = libunivalue.la
test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+test_no_nul_SOURCES = test/no_nul.cpp
+test_no_nul_LDADD = libunivalue.la
+test_no_nul_CXXFLAGS = -I$(top_srcdir)/include
+test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+
TEST_FILES = \
$(TEST_DATA_DIR)/fail10.json \
$(TEST_DATA_DIR)/fail11.json \
@@ -77,6 +82,7 @@ TEST_FILES = \
$(TEST_DATA_DIR)/fail39.json \
$(TEST_DATA_DIR)/fail40.json \
$(TEST_DATA_DIR)/fail41.json \
+ $(TEST_DATA_DIR)/fail42.json \
$(TEST_DATA_DIR)/fail3.json \
$(TEST_DATA_DIR)/fail4.json \
$(TEST_DATA_DIR)/fail5.json \
diff --git a/include/univalue.h b/include/univalue.h
index e8ce283..362bd6f 100644
--- a/include/univalue.h
+++ b/include/univalue.h
@@ -124,9 +124,10 @@ public:
std::string write(unsigned int prettyIndent = 0,
unsigned int indentLevel = 0) const;
+ bool read(const char *raw, size_t len);
bool read(const char *raw);
bool read(const std::string& rawStr) {
- return read(rawStr.c_str());
+ return read(rawStr.data(), rawStr.size());
}
private:
@@ -240,7 +241,7 @@ enum jtokentype {
};
extern enum jtokentype getJsonToken(std::string& tokenVal,
- unsigned int& consumed, const char *raw);
+ unsigned int& consumed, const char *raw, const char *end);
extern const char *uvTypeName(UniValue::VType t);
static inline bool jsonTokenIsValue(enum jtokentype jtt)
diff --git a/lib/univalue.cpp b/lib/univalue.cpp
index 5a2860c..6058622 100644
--- a/lib/univalue.cpp
+++ b/lib/univalue.cpp
@@ -104,7 +104,7 @@ static bool validNumStr(const string& s)
{
string tokenVal;
unsigned int consumed;
- enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str());
+ enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size());
return (tt == JTOK_NUMBER);
}
diff --git a/lib/univalue_read.cpp b/lib/univalue_read.cpp
index 95bac69..81cde4c 100644
--- a/lib/univalue_read.cpp
+++ b/lib/univalue_read.cpp
@@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last,
}
enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
- const char *raw)
+ const char *raw, const char *end)
{
tokenVal.clear();
consumed = 0;
const char *rawStart = raw;
- while ((*raw) && (json_isspace(*raw))) // skip whitespace
+ while (raw < end && (json_isspace(*raw))) // skip whitespace
raw++;
- switch (*raw) {
-
- case 0:
+ if (raw >= end)
return JTOK_NONE;
+ switch (*raw) {
+
case '{':
raw++;
consumed = (raw - rawStart);
@@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
numStr += *raw; // copy first char
raw++;
- if ((*first == '-') && (!json_isdigit(*raw)))
+ if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
return JTOK_ERR;
- while ((*raw) && json_isdigit(*raw)) { // copy digits
+ while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
// part 2: frac
- if (*raw == '.') {
+ if (raw < end && *raw == '.') {
numStr += *raw; // copy .
raw++;
- if (!json_isdigit(*raw))
+ if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
- while ((*raw) && json_isdigit(*raw)) { // copy digits
+ while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
// part 3: exp
- if (*raw == 'e' || *raw == 'E') {
+ if (raw < end && (*raw == 'e' || *raw == 'E')) {
numStr += *raw; // copy E
raw++;
- if (*raw == '-' || *raw == '+') { // copy +/-
+ if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
numStr += *raw;
raw++;
}
- if (!json_isdigit(*raw))
+ if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
- while ((*raw) && json_isdigit(*raw)) { // copy digits
+ while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
@@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
string valStr;
JSONUTF8StringFilter writer(valStr);
- while (*raw) {
+ while (raw < end) {
if ((unsigned char)*raw < 0x20)
return JTOK_ERR;
else if (*raw == '\\') {
raw++; // skip backslash
+ if (raw >= end)
+ return JTOK_ERR;
+
switch (*raw) {
case '"': writer.push_back('\"'); break;
case '\\': writer.push_back('\\'); break;
@@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
case 'u': {
unsigned int codepoint;
- if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
+ if (raw + 1 + 4 >= end ||
+ hatoui(raw + 1, raw + 1 + 4, codepoint) !=
raw + 1 + 4)
return JTOK_ERR;
writer.push_back_u(codepoint);
@@ -246,7 +250,7 @@ enum expect_bits {
#define setExpect(bit) (expectMask |= EXP_##bit)
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
-bool UniValue::read(const char *raw)
+bool UniValue::read(const char *raw, size_t size)
{
clear();
@@ -257,10 +261,11 @@ bool UniValue::read(const char *raw)
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
+ const char* end = raw + size;
do {
last_tok = tok;
- tok = getJsonToken(tokenVal, consumed, raw);
+ tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok == JTOK_NONE || tok == JTOK_ERR)
return false;
raw += consumed;
@@ -432,10 +437,13 @@ bool UniValue::read(const char *raw)
} while (!stack.empty ());
/* Check that nothing follows the initial construct (parsed above). */
- tok = getJsonToken(tokenVal, consumed, raw);
+ tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok != JTOK_NONE)
return false;
return true;
}
+bool UniValue::read(const char *raw) {
+ return read(raw, strlen(raw));
+}
diff --git a/test/.gitignore b/test/.gitignore
index 3d9347f..aea1237 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,4 +1,5 @@
unitester
+no_nul
*.trs
*.log
diff --git a/test/fail42.json b/test/fail42.json
new file mode 100644
index 0000000..9c7565a
Binary files /dev/null and b/test/fail42.json differ
diff --git a/test/no_nul.cpp b/test/no_nul.cpp
new file mode 100644
index 0000000..83d2922
--- /dev/null
+++ b/test/no_nul.cpp
@@ -0,0 +1,8 @@
+#include "univalue.h"
+
+int main (int argc, char *argv[])
+{
+ char buf[] = "___[1,2,3]___";
+ UniValue val;
+ return val.read(buf + 3, 7) ? 0 : 1;
+}
diff --git a/test/unitester.cpp b/test/unitester.cpp
index 05f3842..489ae05 100644
--- a/test/unitester.cpp
+++ b/test/unitester.cpp
@@ -113,6 +113,7 @@ static const char *filenames[] = {
"fail39.json", // invalid unicode: only second half of surrogate pair
"fail40.json", // invalid unicode: broken UTF-8
"fail41.json", // invalid unicode: unfinished UTF-8
+ "fail42.json", // valid json with garbage following a nul byte
"fail3.json",
"fail4.json", // extra comma
"fail5.json",
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-bitcoin/libunivalue.git
More information about the Pkg-bitcoin-commits
mailing list