Commit message: SERVER-17264: improve bson validation for utf-8 strings

(cherry picked from commit 394a8569ff14a215c0691aa34440227b2e62a4de)

Conflicts:
	src/mongo/bson/bson_validate_test.cpp
CVE: CVE-2015-1609
CWE: CWE-20
Vulnerability description: MongoDB before 2.4.13 and 2.6.x before 2.6.8 allows remote attackers to cause a denial of service via a crafted UTF-8 string in a BSON request.

Project: mongo

        Status validateBSONIterative(Buffer* buffer) {
            std::deque<ValidationObjectFrame> frames;
            ValidationObjectFrame* curr = NULL;
            ValidationState::State state = ValidationState::BeginObj;

            while (state != ValidationState::Done) {
                switch (state) {
                case ValidationState::BeginObj:
                    frames.push_back(ValidationObjectFrame());
                    curr = &frames.back();
                    curr->setStartPosition(buffer->position());
                    curr->setIsCodeWithScope(false);
                    if (!buffer->readNumber<int>(&curr->expectedSize)) {
                        return Status(ErrorCodes::InvalidBSON,
                                      "bson size is larger than buffer size");
                    }
                    state = ValidationState::WithinObj;
                    // fall through
                case ValidationState::WithinObj: {
                    Status status = validateElementInfo(buffer, &state);
                    if (!status.isOK())
                        return status;
                    break;
                }
                case ValidationState::EndObj: {
                    int actualLength = buffer->position() - curr->startPosition();
                    if ( actualLength != curr->expectedSize ) {
                        return Status( ErrorCodes::InvalidBSON,
                                       "bson length doesn't match what we found" );
                    }
                    frames.pop_back();
                    if (frames.empty()) {
                        state = ValidationState::Done;
                    }
                    else {
                        curr = &frames.back();
                        if (curr->isCodeWithScope())
                            state = ValidationState::EndCodeWScope;
                        else
                            state = ValidationState::WithinObj;
                    }
                    break;
                }
                case ValidationState::BeginCodeWScope: {
                    frames.push_back(ValidationObjectFrame());
                    curr = &frames.back();
                    curr->setStartPosition(buffer->position());
                    curr->setIsCodeWithScope(true);
                    if ( !buffer->readNumber<int>( &curr->expectedSize ) )
                        return Status( ErrorCodes::InvalidBSON, "invalid bson CodeWScope size" );
                    Status status = buffer->readUTF8String( NULL );
                    if ( !status.isOK() )
                        return status;
                    state = ValidationState::BeginObj;
                    break;
                }
                case ValidationState::EndCodeWScope: {
                    int actualLength = buffer->position() - curr->startPosition();
                    if ( actualLength != curr->expectedSize ) {
                        return Status( ErrorCodes::InvalidBSON,
                                       "bson length for CodeWScope doesn't match what we found" );
                    }
                    frames.pop_back();
                    if (frames.empty())
                        return Status(ErrorCodes::InvalidBSON, "unnested CodeWScope");
                    curr = &frames.back();
                    state = ValidationState::WithinObj;
                    break;
                }
                case ValidationState::Done:
                    break;
                }
            }

            return Status::OK();
        }

Project: mongo

            Status readUTF8String( StringData* out ) {
                int sz;
                if ( !readNumber<int>( &sz ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );

                if ( sz <= 0 ) {
                    // must have NULL at the very least
                    return Status( ErrorCodes::InvalidBSON, "invalid bson");
                }

                if ( out ) {
                    *out = StringData( _buffer + _position, sz );
                }

                if ( !skip( sz - 1 ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );

                char c;
                if ( !readNumber<char>( &c ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );

                if ( c != 0 )
                    return Status( ErrorCodes::InvalidBSON, "not null terminate string" );

                return Status::OK();
            }

Project: mongo

        Status validateElementInfo(Buffer* buffer, ValidationState::State* nextState) {
            Status status = Status::OK();

            char type;
            if ( !buffer->readNumber<char>(&type) )
                return Status( ErrorCodes::InvalidBSON, "invalid bson" );

            if ( type == EOO ) {
                *nextState = ValidationState::EndObj;
                return Status::OK();
            }

            StringData name;
            status = buffer->readCString( &name );
            if ( !status.isOK() )
                return status;

            switch ( type ) {
            case MinKey:
            case MaxKey:
            case jstNULL:
            case Undefined:
                return Status::OK();

            case jstOID:
                if ( !buffer->skip( sizeof(OID) ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );
                return Status::OK();

            case NumberInt:
                if ( !buffer->skip( sizeof(int32_t) ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );
                return Status::OK();

            case Bool:
                if ( !buffer->skip( sizeof(int8_t) ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );
                return Status::OK();


            case NumberDouble:
            case NumberLong:
            case Timestamp:
            case Date:
                if ( !buffer->skip( sizeof(int64_t) ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );
                return Status::OK();

            case DBRef:
                status = buffer->readUTF8String( NULL );
                if ( !status.isOK() )
                    return status;
                buffer->skip( sizeof(OID) );
                return Status::OK();

            case RegEx:
                status = buffer->readCString( NULL );
                if ( !status.isOK() )
                    return status;
                status = buffer->readCString( NULL );
                if ( !status.isOK() )
                    return status;

                return Status::OK();

            case Code:
            case Symbol:
            case String:
                status = buffer->readUTF8String( NULL );
                if ( !status.isOK() )
                    return status;
                return Status::OK();

            case BinData: {
                int sz;
                if ( !buffer->readNumber<int>( &sz ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );
                if ( !buffer->skip( 1 + sz ) )
                    return Status( ErrorCodes::InvalidBSON, "invalid bson" );
                return Status::OK();
            }
            case CodeWScope:
                *nextState = ValidationState::BeginCodeWScope;
                return Status::OK();
            case Object:
            case Array:
                *nextState = ValidationState::BeginObj;
                return Status::OK();

            default:
                return Status( ErrorCodes::InvalidBSON, "invalid bson type" );
            }
        }