vendredi 13 avril 2018

Possible bug in gcc when using member initializer

I am working on a project where I use 0MQ, and hence the zeromq tag.

I am experiencing a weird problem in my code which I am not sure is a bug in g++ or in my wrapping the 0MQ library. I hope that I can get some help from you. Basically, I am testing against

~> g++ --version
g++ (GCC) 7.3.1 20180312
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

~> clang++ --version
clang version 6.0.0 (tags/RELEASE_600/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

You can find the zmq.hpp file in my GitHub account, which I did not want to paste here because of its length. My minimal working example based on that header would then read:

#include <iostream>
#include <string>

#include "zmq.hpp"

int main(int argc, char *argv[]) {
  auto version = zmq::version();
  std::cout << "0MQ version: v" << std::get<0>(version) << '.'
            << std::get<1>(version) << '.' << std::get<2>(version) << '\n';

  zmq::message msg1, msg2;
  std::string p1{"part 1"};
  uint16_t p2{5};
  msg1.addpart(std::begin(p1), std::end(p1));
  msg1.addpart(p2);

  std::cout << "msg1 is a " << msg1.numparts() << "-part message.\n";
  std::cout << "msg1[0]: " << static_cast<char *>(msg1.data(0)) << '\n';
  std::cout << "msg1[1]: " << *static_cast<uint16_t *>(msg1.data(1)) << '\n';

  msg2 = ;

  std::cout << "msg2 is a " << msg2.numparts() << "-part message.\n";
  std::cout << "msg2[0]: " << static_cast<char *>(msg2.data(0)) << '\n';
  std::cout << "msg2[1]: " << *static_cast<uint16_t *>(msg2.data(1)) << '\n';

  return 0;
}

When I compile the code with

~> clang++ -Wall -std=c++11 -O3 mwe.cpp -o mwe.out -lzmq
~> ./mwe.out

I see the following output:

0MQ version: v4.2.5
msg1 is a 2-part message.
msg1[0]: part 1
msg1[1]: 5
msg2 is a 2-part message.
msg2[0]: part 1
msg2[1]: 5

However, when I compile the code with

~> g++ -Wall -std=c++11 -O3 mwe.cpp -o mwe.out -lzmq
~> ./mwe.out

I get the following:

0MQ version: v4.2.5
msg1 is a 2-part message.
msg1[0]: part 1
msg1[1]: 5
msg2 is a 1-part message.
msg2[0]: <some garbage here>
fish: “./mwe.out” terminated by signal SIGSEGV (Address boundary error)

Obviously, I am getting SIGSEGV due to my reading a memory location that I do not own. The interesting part is that when I change Line 764 of the zmq.hpp file to read:

// message::message(std::vector<part> parts) noexcept : parts_{std::move(parts)} {}
message::message(std::vector<part> parts) noexcept {
  parts_ = std::move(parts);
}

the code works as intended when compiled with both of the compilers.

In short, I would like to know if I am doing something fishy that results in the g++-compiled code's not working, or there is a possibility that g++ has some bug. g++ does not have the same behavior with simple dummy structs that I use (that's why I could not write an MWE with simpler structs, and that's why I do suspect my wrappers). And, the same behavior is also observed with -O0 -g switches.

Thank you in advance for your time.

Aucun commentaire:

Enregistrer un commentaire