From 40199d64b7e8ff2c955e130b7673d8c2b182b8c8 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 20 Mar 2013 11:48:33 +0100 Subject: [PATCH] fixed handling of empty and reserved attribute names in documents specifying an empty attribute name in a document may have led to segmentation faults when the document was unpacked later. --- .../HttpInterface/api-attributes-spec.rb | 163 +++++++++++++++ UnitTests/HttpInterface/run-tests | 1 + UnitTests/Makefile.unittests | 1 + arangod/V8Server/v8-vocbase.cpp | 16 +- arangod/VocBase/key-generator.c | 4 + html/admin/js/bootstrap/module-internal.js | 2 +- js/common/bootstrap/module-internal.js | 2 +- js/common/tests/shell-attributes.js | 188 ++++++++++++++++++ lib/JsonParser/json-parser.c | 46 ++--- lib/JsonParser/json-parser.l | 2 +- lib/ShapedJson/shaped-json.c | 20 +- lib/V8/v8-conv.cpp | 13 +- 12 files changed, 415 insertions(+), 43 deletions(-) create mode 100644 UnitTests/HttpInterface/api-attributes-spec.rb create mode 100644 js/common/tests/shell-attributes.js diff --git a/UnitTests/HttpInterface/api-attributes-spec.rb b/UnitTests/HttpInterface/api-attributes-spec.rb new file mode 100644 index 0000000000..a5545e0ee2 --- /dev/null +++ b/UnitTests/HttpInterface/api-attributes-spec.rb @@ -0,0 +1,163 @@ +# coding: utf-8 + +require 'rspec' +require './arangodb.rb' + +describe ArangoDB do + api = "/_api/document" + prefix = "attributes" + + context "dealing with attribute names" do + + before do + @cn = "UnitTestsCollectionAttributes" + + ArangoDB.drop_collection(@cn) + @cid = ArangoDB.create_collection(@cn) + end + + after do + ArangoDB.drop_collection(@cn) + end + +################################################################################ +## creates a document with an empty attribute +################################################################################ + + it "creates a document with an empty attribute name" do + cmd = api + "?collection=" + @cn + body = "{ \"\" : \"a\", \"foo\" : \"b\" }" + doc = ArangoDB.log_post("#{prefix}-create-empty-name", cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + doc.parsed_response['error'].should eq(false) + + id = doc.parsed_response['_id'] + + cmd = api + "/" + id + doc = ArangoDB.log_get("#{prefix}-create-empty-name", cmd) + + doc.parsed_response.should_not have_key('') + doc.parsed_response.should have_key('foo') + end + +################################################################################ +## queries a document with an empty attribute +################################################################################ + + it "queries a document with an empty attribute name" do + cmd = api + "?collection=" + @cn + body = "{ \"\" : \"a\", \"foo\" : \"b\" }" + doc = ArangoDB.log_post("#{prefix}-query-empty-name", cmd, :body => body) + + doc.code.should eq(201) + doc.parsed_response['error'].should eq(false) + + cmd = "/_api/simple/all" + body = "{ \"collection\" : \"" + @cn + "\" }" + doc = ArangoDB.log_put("#{prefix}-query-empty-name", cmd, :body => body) + + documents = doc.parsed_response['result'] + + documents.length.should eq(1) + documents[0].should_not have_key('') + documents[0].should have_key('foo') + end + +################################################################################ +## creates a document with reserved attribute names +################################################################################ + + it "creates a document with reserved attribute names" do + cmd = api + "?collection=" + @cn + body = "{ \"_rev\" : \"99\", \"foo\" : \"002\", \"_id\" : \"meow\", \"_from\" : \"a\", \"_to\" : \"b\", \"_test\" : \"c\", \"meow\" : \"d\" }" + doc = ArangoDB.log_post("#{prefix}-create-reserved-names", cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + doc.parsed_response['error'].should eq(false) + + id = doc.parsed_response['_id'] + + cmd = api + "/" + id + doc = ArangoDB.log_get("#{prefix}-create-reserved-names", cmd) + + doc.parsed_response['_id'].should eq(id) + doc.parsed_response['_rev'].should_not eq('99') + doc.parsed_response.should_not have_key('_from') + doc.parsed_response.should_not have_key('_to') + doc.parsed_response.should_not have_key('_test') + doc.parsed_response.should have_key('meow') + doc.parsed_response['meow'].should eq('d') + doc.parsed_response['foo'].should eq('002') + end + +################################################################################ +## nested attribute names +################################################################################ + + it "creates a document with nested attribute names" do + cmd = api + "?collection=" + @cn + body = "{ \"a\" : \"1\", \"b\" : { \"b\" : \"2\" , \"a\" : \"3\", \"\": \"4\", \"_from\": \"5\", \"c\" : 6 } }" + doc = ArangoDB.log_post("#{prefix}-create-duplicate-names", cmd, :body => body) + + doc.code.should eq(201) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + + id = doc.parsed_response['_id'] + + cmd = api + "/" + id + doc = ArangoDB.log_get("#{prefix}-create-empty-name", cmd) + + doc.parsed_response.should have_key('a') + doc.parsed_response['a'].should eq('1') + doc.parsed_response.should have_key('b') + + doc.parsed_response['b'].should_not have_key('') + doc.parsed_response['b'].should_not have_key('_from') + doc.parsed_response['b'].should have_key('b') + doc.parsed_response['b'].should have_key('a') + doc.parsed_response['b'].should have_key('c') + doc.parsed_response['b'].should eq({ "b" => "2", "a" => "3", "c" => 6 }) + end + +################################################################################ +## duplicate attribute names +################################################################################ + + it "creates a document with duplicate attribute names" do + cmd = api + "?collection=" + @cn + body = "{ \"a\" : \"1\", \"b\" : \"2\", \"a\" : \"3\" }" + doc = ArangoDB.log_post("#{prefix}-create-duplicate-names", cmd, :body => body) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + doc.parsed_response['errorNum'].should eq(600) + end + +################################################################################ +## nested duplicate attribute names +################################################################################ + + it "creates a document with nested duplicate attribute names" do + cmd = api + "?collection=" + @cn + body = "{ \"a\" : \"1\", \"b\" : { \"b\" : \"2\" , \"c\" : \"3\", \"b\": \"4\" } }" + doc = ArangoDB.log_post("#{prefix}-create-duplicate-names-nested", cmd, :body => body) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + doc.parsed_response['errorNum'].should eq(600) + end + + end +end diff --git a/UnitTests/HttpInterface/run-tests b/UnitTests/HttpInterface/run-tests index b1c7f95567..733a4a1d91 100755 --- a/UnitTests/HttpInterface/run-tests +++ b/UnitTests/HttpInterface/run-tests @@ -4,6 +4,7 @@ test -d logs || mkdir logs rspec --color --format d \ api-http-spec.rb \ api-admin-spec.rb \ + api-attributes-spec.rb \ api-batch-spec.rb \ api-collection-spec.rb \ api-graph-spec.rb \ diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 567793a169..7db33c66d6 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -210,6 +210,7 @@ endif ################################################################################ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-require.js \ + @top_srcdir@/js/common/tests/shell-attributes.js \ @top_srcdir@/js/common/tests/shell-document.js \ @top_srcdir@/js/common/tests/shell-edge.js \ @top_srcdir@/js/common/tests/shell-database.js \ diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 300651e9cd..344fcc84f1 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -1000,6 +1000,7 @@ static v8::Handle SaveVocbaseCol (SingleCollectionWriteTransactionprimaryCollection(); TRI_shaped_json_t* shaped = TRI_ShapedJsonV8Object(argv[0], primary->_shaper); + if (! holder.registerShapedJson(primary->_shaper, shaped)) { return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_errno(), @@ -1999,7 +2000,7 @@ static v8::Handle JS_CreateCursor (v8::Arguments const& argv) { TRI_CreateErrorObject(TRI_ERROR_ILLEGAL_OPTION, " must be a list"))); } - + // extract objects v8::Handle array = v8::Handle::Cast(argv[0]); TRI_json_t* json = TRI_ObjectToJson(array); @@ -6006,10 +6007,11 @@ static v8::Handle MapGetShapedJson (v8::Local name, TRI_primary_collection_t* collection = barrier->_container->_collection; // convert the JavaScript string to a string - string key = TRI_ObjectToString(name); + const string key = TRI_ObjectToString(name); - if (key == "") { - return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_ARANGO_ILLEGAL_NAME, "name must not be empty"))); + if (key.size() == 0) { + // we must not throw a v8 exception here because this will cause follow up errors + return scope.Close(v8::Handle()); } if (TRI_IsSystemCollectionName(key.c_str())) { @@ -6020,9 +6022,6 @@ static v8::Handle MapGetShapedJson (v8::Local name, TRI_shaper_t* shaper = collection->_shaper; TRI_shape_pid_t pid = shaper->findAttributePathByName(shaper, key.c_str()); - // TRI_shape_sid_t sid; - // TRI_EXTRACT_SHAPE_IDENTIFIER_MARKER(sid, marker); - TRI_shaped_json_t document; TRI_EXTRACT_SHAPED_JSON_MARKER(document, marker); @@ -6040,7 +6039,8 @@ static v8::Handle MapGetShapedJson (v8::Local name, } } else { - return scope.Close(v8::ThrowException(v8::String::New("cannot extract attribute"))); + // we must not throw a v8 exception here because this will cause follow up errors + return scope.Close(v8::Handle()); } } diff --git a/arangod/VocBase/key-generator.c b/arangod/VocBase/key-generator.c index 95c92ff291..51076cbc4a 100644 --- a/arangod/VocBase/key-generator.c +++ b/arangod/VocBase/key-generator.c @@ -193,6 +193,10 @@ static int TraditionalGenerate (TRI_key_generator_t* const generator, // user key is too long return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD; } + else if (userKeyLength == 0) { + // user key is empty + return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD; + } // validate user-supplied key if (regexec(&data->_regex, userKey, 0, NULL, 0) != 0) { diff --git a/html/admin/js/bootstrap/module-internal.js b/html/admin/js/bootstrap/module-internal.js index 53ce2f3d9c..b908b22b77 100644 --- a/html/admin/js/bootstrap/module-internal.js +++ b/html/admin/js/bootstrap/module-internal.js @@ -483,7 +483,7 @@ //////////////////////////////////////////////////////////////////////////////// var quoteSingleJsonCharacter = function (c) { - if (characterQuoteCache.hasOwnProperty[c]) { + if (characterQuoteCache.hasOwnProperty(c)) { return characterQuoteCache[c]; } diff --git a/js/common/bootstrap/module-internal.js b/js/common/bootstrap/module-internal.js index 53ce2f3d9c..b908b22b77 100644 --- a/js/common/bootstrap/module-internal.js +++ b/js/common/bootstrap/module-internal.js @@ -483,7 +483,7 @@ //////////////////////////////////////////////////////////////////////////////// var quoteSingleJsonCharacter = function (c) { - if (characterQuoteCache.hasOwnProperty[c]) { + if (characterQuoteCache.hasOwnProperty(c)) { return characterQuoteCache[c]; } diff --git a/js/common/tests/shell-attributes.js b/js/common/tests/shell-attributes.js new file mode 100644 index 0000000000..7ff6ae9e7e --- /dev/null +++ b/js/common/tests/shell-attributes.js @@ -0,0 +1,188 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test attribute naming +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); + +var arangodb = require("org/arangodb"); + +var db = arangodb.db; +var wait = require("internal").wait; + +// ----------------------------------------------------------------------------- +// --SECTION-- attributes +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test attributes +//////////////////////////////////////////////////////////////////////////////// + +function AttributesSuite () { + var cn = "UnitTestsCollectionAttributes"; + var c = null; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + db._drop(cn); + c = db._create(cn); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + c.unload(); + c.drop(); + c = null; + wait(0.0); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief no attributes +//////////////////////////////////////////////////////////////////////////////// + + testNoAttributes : function () { + var doc = { }; + + var d1 = c.save(doc); + var d2 = c.document(d1._id); + delete d1.error; + + assertEqual(d1, d2); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief empty attribute name +//////////////////////////////////////////////////////////////////////////////// + + testEmptyAttribute : function () { + var doc = { "" : "foo" }; + + var d1 = c.save(doc); + var d2 = c.document(d1._id); + delete d1.error; + + var i; + for (i in d2) { + if (d2.hasOwnProperty(i)) { + assertTrue(i !== ""); + } + } + + assertEqual(d1, d2); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief query empty attribute name +//////////////////////////////////////////////////////////////////////////////// + + testQueryEmptyAttribute : function () { + var doc = { "" : "foo" }; + c.save(doc); + + var docs = c.toArray(); + assertEqual(1, docs.length); + var d = docs[0]; + + var i; + for (i in d) { + if (d.hasOwnProperty(i)) { + assertTrue(i !== ""); + } + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief reserved attribute names +//////////////////////////////////////////////////////////////////////////////// + + testReservedAttributes : function () { + var doc = { "_id" : "foo", "_rev": "99", "_key" : "meow", "_from" : "33", "_to": "99", "_test" : false }; + + var d1 = c.save(doc); + var d2 = c.document(d1._id); + + assertEqual("meow", d1._key); + assertEqual("meow", d2._key); + assertEqual(cn + "/meow", d1._id); + assertEqual(cn + "/meow", d2._id); + assertEqual(d1._rev, d2._rev); + + // user specified _rev value must have been ignored + assertTrue(d1._rev !== "99"); + + // test attributes + var i; + for (i in d2) { + if (d2.hasOwnProperty(i)) { + assertTrue(i !== "_from" && i !== "_to" && i !== "_test"); + } + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief attribute name with special chars +//////////////////////////////////////////////////////////////////////////////// + + testSpecialAttributes : function () { + var doc = { "-meow-" : 1, "mötör" : 2, " " : 3, "\t" : 4, "\r" : 5, "\n" : 6 }; + + var d1 = c.save(doc); + var d2 = c.document(d1._id); + + assertEqual(1, d2["-meow-"]); + assertEqual(2, d2["mötör"]); + assertEqual(3, d2[" "]); + assertEqual(4, d2["\t"]); + assertEqual(5, d2["\r"]); + assertEqual(6, d2["\n"]); + } + + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- main +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(AttributesSuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: diff --git a/lib/JsonParser/json-parser.c b/lib/JsonParser/json-parser.c index b8b7393c3b..d8fce82053 100644 --- a/lib/JsonParser/json-parser.c +++ b/lib/JsonParser/json-parser.c @@ -89,7 +89,6 @@ typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; -typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; @@ -213,11 +212,6 @@ typedef void* yyscan_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif - #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 @@ -240,6 +234,11 @@ typedef size_t yy_size_t; #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state @@ -257,7 +256,7 @@ struct yy_buffer_state /* Number of characters read into yy_ch_buf, not including EOB * characters. */ - yy_size_t yy_n_chars; + int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to @@ -336,7 +335,7 @@ static void tri_jsp__init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscann YY_BUFFER_STATE tri_jsp__scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE tri_jsp__scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); -YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); +YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); void *tri_jsp_alloc (yy_size_t ,yyscan_t yyscanner ); void *tri_jsp_realloc (void *,yy_size_t ,yyscan_t yyscanner ); @@ -387,7 +386,7 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ - yyleng = (yy_size_t) (yy_cp - yy_bp); \ + yyleng = (size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; @@ -566,8 +565,8 @@ struct yyguts_t size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; - yy_size_t yy_n_chars; - yy_size_t yyleng_r; + int yy_n_chars; + int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; @@ -614,7 +613,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner ); void tri_jsp_set_out (FILE * out_str ,yyscan_t yyscanner ); -yy_size_t tri_jsp_get_leng (yyscan_t yyscanner ); +int tri_jsp_get_leng (yyscan_t yyscanner ); char *tri_jsp_get_text (yyscan_t yyscanner ); @@ -673,7 +672,7 @@ static int input (yyscan_t yyscanner ); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - yy_size_t n; \ + int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ @@ -1129,7 +1128,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) else { - yy_size_t num_to_read = + int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) @@ -1143,7 +1142,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) if ( b->yy_is_our_buffer ) { - yy_size_t new_size = b->yy_buf_size * 2; + int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; @@ -1174,7 +1173,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - yyg->yy_n_chars, num_to_read ); + yyg->yy_n_chars, (int) num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } @@ -1299,7 +1298,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) else { /* need more input */ - yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) @@ -1323,7 +1322,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) case EOB_ACT_END_OF_FILE: { if ( tri_jsp_wrap(yyscanner ) ) - return 0; + return EOF; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; @@ -1585,7 +1584,7 @@ void tri_jsp_pop_buffer_state (yyscan_t yyscanner) */ static void tri_jsp_ensure_buffer_stack (yyscan_t yyscanner) { - yy_size_t num_to_alloc; + int num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { @@ -1683,11 +1682,12 @@ YY_BUFFER_STATE tri_jsp__scan_string (yyconst char * yystr , yyscan_t yyscanner) * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) +YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; - yy_size_t n, i; + yy_size_t n; + int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; @@ -1797,7 +1797,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner) /** Get the length of the current token. * @param yyscanner The scanner object. */ -yy_size_t tri_jsp_get_leng (yyscan_t yyscanner) +int tri_jsp_get_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; @@ -2184,7 +2184,7 @@ static TRI_json_t* ParseArray (yyscan_t scanner) { // do proper unescaping name = TRI_UnescapeUtf8StringZ(yyextra._memoryZone, yytext + 1, nameLen, &outLength); - + nameLen = outLength; } else if (c == STRING_CONSTANT_ASCII) { // ASCII-only attribute name diff --git a/lib/JsonParser/json-parser.l b/lib/JsonParser/json-parser.l index 1cc17ef853..f94b0a0e81 100644 --- a/lib/JsonParser/json-parser.l +++ b/lib/JsonParser/json-parser.l @@ -286,7 +286,7 @@ static TRI_json_t* ParseArray (yyscan_t scanner) { // do proper unescaping name = TRI_UnescapeUtf8StringZ(yyextra._memoryZone, yytext + 1, nameLen, &outLength); - + nameLen = outLength; } else if (c == STRING_CONSTANT_ASCII) { // ASCII-only attribute name diff --git a/lib/ShapedJson/shaped-json.c b/lib/ShapedJson/shaped-json.c index e7dd8842e4..dde16a315b 100644 --- a/lib/ShapedJson/shaped-json.c +++ b/lib/ShapedJson/shaped-json.c @@ -310,6 +310,8 @@ static int WeightShapeType (TRI_shape_type_t type) { case TRI_SHAPE_HOMOGENEOUS_LIST: return 900; } + LOG_ERROR("invalid shape type: %d\n", (int) type); + assert(false); return 0; } @@ -780,8 +782,8 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, T char* ptr; // sanity checks - assert(json->_type == TRI_JSON_ARRAY); - assert(json->_value._objects._length % 2 == 0); + TRI_ASSERT_DEBUG(json->_type == TRI_JSON_ARRAY); + TRI_ASSERT_DEBUG(json->_value._objects._length % 2 == 0); // number of attributes n = json->_value._objects._length / 2; @@ -805,6 +807,17 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, T key = TRI_AtVector(&json->_value._objects, 2 * i); val = TRI_AtVector(&json->_value._objects, 2 * i + 1); + TRI_ASSERT_DEBUG(key != NULL); + TRI_ASSERT_DEBUG(val != NULL); + + if (key->_value._string.data == NULL || + key->_value._string.length == 1 || + key->_value._string.data[0] == '_') { + // empty or reserved attribute name + p--; + continue; + } + // first find an identifier for the name p->_aid = shaper->findAttributeName(shaper, key->_value._string.data); @@ -841,6 +854,9 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, T // add variable offset table size total += (v + 1) * sizeof(TRI_shape_size_t); + // now adjust n because we might have excluded empty attributes + n = f + v; + // now sort the shape entries TRI_SortShapeValues(values, n); diff --git a/lib/V8/v8-conv.cpp b/lib/V8/v8-conv.cpp index 72a2fbf99d..89e86a0b32 100644 --- a/lib/V8/v8-conv.cpp +++ b/lib/V8/v8-conv.cpp @@ -289,8 +289,6 @@ static bool FillShapeValueList (TRI_shaper_t* shaper, return false; } - memset(values, 0, sizeof(TRI_shape_value_t) * n); - total = 0; e = values + n; @@ -592,7 +590,7 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, // first find an identifier for the name TRI_Utf8ValueNFC keyStr(TRI_UNKNOWN_MEM_ZONE, key); - if (*keyStr == 0) { + if (*keyStr == 0 || keyStr.length() == 0) { --p; continue; } @@ -789,13 +787,12 @@ static bool FillShapeValueJson (TRI_shaper_t* shaper, return FillShapeValueNull(shaper, dst); } } - - seenObjects.push_back(o); } else { seenHashes.insert(hash); - seenObjects.push_back(o); } + + seenObjects.push_back(o); } if (json->IsNull()) { @@ -1439,6 +1436,7 @@ TRI_json_t* TRI_ObjectToJson (v8::Handle parameter) { const uint32_t n = arrayParameter->Length(); TRI_json_t* listJson = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, (const size_t) n); + if (listJson != 0) { for (uint32_t j = 0; j < n; ++j) { v8::Handle item = arrayParameter->Get(j); @@ -1458,9 +1456,10 @@ TRI_json_t* TRI_ObjectToJson (v8::Handle parameter) { const uint32_t n = names->Length(); TRI_json_t* arrayJson = TRI_CreateArray2Json(TRI_UNKNOWN_MEM_ZONE, (const size_t) n); + if (arrayJson != 0) { for (uint32_t j = 0; j < n; ++j) { - v8::Handle key = names->Get(j); + v8::Handle key = names->Get(j); v8::Handle item = arrayParameter->Get(key); TRI_json_t* result = TRI_ObjectToJson(item);