Skip to content

Dart produces flatbuffers which cannot be verified in C++ [C++, Dart, 1.9.0+] #4858

Description

@paulreimer

Very odd behaviour when generating flatbuffers from Dart for consumption from C++ (anywhere, really). Certain combinations of field sizes produce binary data which C++ can verify, but other combinations (e.g. just add or remove a few characters from a string, or omit/add an extra field), and then the buffer does not verify anymore. Binary-wise -- valid or not -- the binaries generated from dart never seem to match the output from flatc, just upon a visual inspection with xxd.

So for example, a user name field set to "Foo Bar1" will not verify, but a user name of "Foo Bar" will. ("Foo Bar123" might be good again, who knows?!)

I made a reproducible test case (on my dev machine, but I originally ran into it on an embedded platform on the C++ side and cloud platform on the Dart side, but that is difficult to repro).

tree .
.
├── pubspec.yaml
├── repro.cpp
├── repro.dart
└── repro.fbs

cat pubspec.yaml:

name: repro

environment:
  sdk: '>=2.0.0-dev.55.0 <2.0.0'

dependencies:
  flat_buffers: ^1.9.0

cat repro.cpp:

#include <flatbuffers/flatbuffers.h>

#include <fstream>

#include "repro_generated.h"

auto main()
  -> int
{
  std::ifstream stream("repro.fb", std::ios::in | std::ios::binary);
  std::vector<uint8_t> repro_fb(
    (std::istreambuf_iterator<char>(stream)),
    std::istreambuf_iterator<char>()
  );

  flatbuffers::Verifier verifier(repro_fb.data(), repro_fb.size());

  if (verifier.VerifyBuffer<Repro::User>("repr"))
  {
    printf("valid user = true\n");
  }

  return 0;
}

cat repro.dart:

import "dart:io";
import "repro_repro_generated.dart" as Repro;

const String REPRO_FILE_IDENTIFIER = "repr";

void main() {
  final userBuilder = new Repro.UserObjectBuilder(
    name: "Foo Bar1",
    id: "123",
  );

  final userBytes = userBuilder.toBytes(REPRO_FILE_IDENTIFIER);
  if (userBytes == null) {
    throw ("Invalid (empty) User flatbuffer");
  }

  final filename = "repro.fb";
  new File(filename).writeAsBytes(userBytes).then((File file) {
    final user = new Repro.User(userBytes);
    print("got User: ${user}");
  });
}

cat repro.fbs:

namespace Repro;

table User
{
  name:string;
  id:string;
}

root_type User;
file_identifier "repr";
file_extension "fb";

To run it, do this:

pub get
flatc --cpp --dart -o . repro.fbs
c++ -std=c++14 repro.cpp -o repro
dart repro.dart && ./repro

The behaviour is erratic but if you add some characters on the end of the name field in the repro.dart file, the test will pass or fail based on the length (maybe it has to do with alignment but I didn't expect that for a string?)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions