[SERVER-9605] HexData(0, "000000000000000000000005") will throw Created: 07/May/13  Updated: 11/Jul/16  Resolved: 10/Jun/13

Status: Closed
Project: Core Server
Component/s: JavaScript
Affects Version/s: 2.4.1
Fix Version/s: 2.4.5, 2.5.1

Type: Bug Priority: Minor - P4
Reporter: Huy Nguyen Assignee: Tad Marshall
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

AWS m2.2xlarge (AMI: ami-8e27adbe) Production Amazon Linux. RAID10 8 drives on provisioned IOPS data partition. logs partition separate from data


Issue Links:
Related
is related to SERVER-9583 V8 allows BinData() with no arguments Closed
is related to SERVER-8819 Add round trip unit jstest framework ... Closed
is related to SERVER-8820 Add jstest to test argument checking ... Closed
Backwards Compatibility: Fully Compatible
Operating System: ALL
Steps To Reproduce:

mongos> var hid = db.User.findOne({Fn:"Huy"})._id
mongos> hid
BinData(0,"AAAAAAAAAAAAAAAF")
mongos> hid.hex()
000000000000000000000005
mongos> hid.subtype()
0
mongos> parseInt(hid.hex(),16)
5
mongos>

//So then the following should work!

mongos> HexData(hid.subtype(),hid.hex())
Tue May  7 08:35:34.387   Assertion failure false src/mongo/util/hex.h 34
0x747301 0x70ff5d 0x7024b0 0x702600 0x6e9fc0 0x82cac2 0x7413a06362
 mongo(_ZN5mongo15printStackTraceERSo+0x21) [0x747301]
 mongo(_ZN5mongo12verifyFailedEPKcS1_j+0xfd) [0x70ff5d]
 mongo() [0x7024b0]
 mongo(_ZN5mongo11hexDataInitEPNS_7V8ScopeERKN2v89ArgumentsE+0xc0) [0x702600]
 mongo(_ZN5mongo7V8Scope10v8CallbackERKN2v89ArgumentsE+0xb0) [0x6e9fc0]
 mongo() [0x82cac2]
 [0x7413a06362]
Tue May  7 08:35:34.391 JavaScript execution failed: Error: assertion src/mongo/util/hex.h:34
mongos>

//But it throws...

Participants:

 Description   

Our .net client stores a long id of 5. Before saving the id however, we convert it to big endian byte[] of 12 in length (to be the same size of mongo object id) for future proofing. This however should not matter as the following is clearly self explanatory.

See "steps to reproduce"

I actually use these JS functions as a work-around which allows me to call:

mongos> BinData(0, hexToBase64(pad((5).toString(16), 24, 0)))
BinData(0,"AAAAAAAAAAAAAAAF")
 
//custom JS function definition begins:
 
function pad(n, width, z) {
  z = z || '0';
  n = n + '';
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
 
  function btoa(bin) {
	var tableStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	var table = tableStr.split("");
    for (var i = 0, j = 0, len = bin.length / 3, base64 = []; i < len; ++i) {
      var a = bin.charCodeAt(j++), b = bin.charCodeAt(j++), c = bin.charCodeAt(j++);
      if ((a | b | c) > 255) throw new Error("String contains an invalid character");
      base64[base64.length] = table[a >> 2] + table[((a << 4) & 63) | (b >> 4)] +
                              (isNaN(b) ? "=" : table[((b << 2) & 63) | (c >> 6)]) +
                              (isNaN(b + c) ? "=" : table[c & 63]);
    }
    return base64.join("");
  };
 
function hexToBase64(str) {
  return btoa(String.fromCharCode.apply(null,
    str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))
  );
}



 Comments   
Comment by auto [ 19/Jun/13 ]

Author:

{u'username': u'tadmarshall', u'name': u'Tad Marshall', u'email': u'tad@10gen.com'}

Message: SERVER-9605 Use correct string length in hexToBinData()

Do not use hard-coded length of 16 to process variable length hex string.
Branch: v2.4
https://github.com/mongodb/mongo/commit/41ce3764cf4fec472f7d0e7655bda7ecd6f7c662

Comment by Tad Marshall [ 10/Jun/13 ]

Tests that will be covered by SERVER-9583 will handle this case, so we don't need or want an additional test for this ticket.

Comment by Shaun Verch [ 14/May/13 ]

After SERVER-8819 and SERVER-8820, we have frameworks for testing validity and round tripping of various shell constructs.

Comment by Tad Marshall [ 07/May/13 ]

The above commit fixes the bug. Leaving this ticket open until a regression test is written.

Comment by auto [ 07/May/13 ]

Author:

{u'date': u'2013-05-07T11:23:40Z', u'name': u'Tad Marshall', u'email': u'tad@10gen.com'}

Message: SERVER-9605 Use correct string length in hexToBinData()

Do not use hard-coded length of 16 to process variable length hex string.
Branch: master
https://github.com/mongodb/mongo/commit/5c2de15471b7485a37290828dbdee97f068424e8

Comment by Tad Marshall [ 07/May/13 ]

Hi Huy,

Thanks for the report!

I reproduced it in the current code.

This is a bug in the V8 version of hexToBinData() in src/mongo/scripting/v8_dp.cpp lines 701 to 716:

    static v8::Handle<v8::Value> hexToBinData(V8Scope* scope, v8::Local<v8::Object> it, int type,
                                              string hexstr) {
        int len = hexstr.length() / 2;
        scoped_array<char> data(new char[16]);
        const char* src = hexstr.c_str();
        for(int i = 0; i < 16; i++) {
            data[i] = fromHex(src + i * 2);
        }
 
        string encoded = base64::encode(data.get(), 16);
        it->ForceSet(v8::String::New("len"), v8::Number::New(len));
        it->ForceSet(v8::String::New("type"), v8::Number::New(type));
        it->SetHiddenValue(v8::String::New("__BinData"), v8::Number::New(1));
        it->SetInternalField(0, v8::String::New(encoded.c_str(), encoded.length()));
        return it;
    }

It is using a hard-coded length of 16 bytes, even though it just calculated the correct length of the string.

The SpiderMonkey version doesn't have this problem; src/mongo/scripting/engine_spidermonkey.cpp lines 907 to 926:

    static void hexToBinData( JSContext* cx,
                              Convertor* c,
                              jsval* rval,
                              int subtype,
                              const string& s ) {
        JSObject * o = JS_NewObject( cx , &bindata_class , 0 , 0 );
        CHECKNEWOBJECT(o,_context,"Bindata_BinData1");
        size_t s_size = s.size();
        int len = s_size / 2;
        boost::scoped_array<char> data( new char[len] );
        char* p = data.get();
        const char *src = s.c_str();
        for( size_t i = 0; i+1 < s_size; i += 2 ) { 
            *p++ = fromHex( src + i );
        }
        verify( JS_SetPrivate( cx, o, new BinDataHolder( data.get(), len ) ) );
        c->setProperty( o, "len", c->toval( static_cast<double>(len) ) );
        c->setProperty( o, "type", c->toval( static_cast<double>(subtype) ) );
        *rval = OBJECT_TO_JSVAL( o );
    }

Changing the V8 version to use the calculated length fixes the problem.

Tad

Generated at Thu Feb 08 03:20:55 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.