#原因
oflag
にO_CREAT
をしているにも関わらず、2つの追加引数mode
, attr
が指定されておらず、CでもC++でも動作が未定義になるからです。C++では運良くたまたま動いていただけで、運が悪ければCでもC++でもsegmentation faultで落ちたりすることもあるかもしれないということです。
##原因詳細
ubuntu 20.04のmq_open
のマニュアルを見ますと、
sh
1$ man mq_open
2...
3 mqd_t mq_open(const char *name, int oflag);
4 mqd_t mq_open(const char *name, int oflag, mode_t mode,
5 struct mq_attr *attr);
6...
7 If O_CREAT is specified in oflag, then two additional arguments must be supplied. The mode argument specifies the permissions to be placed on the new queue, as for open(2).
8...
という記述が出てきます。debianでも大きくは違わないと思います(ご確認ください)。
関数の宣言が2つあってC++ならオーバーロードで実装しそうな形になっていますが(すみません。C++がお得意なのかと思いこういう説明で入りましたが、この問題自体にオーバーロードは関係ありません)、このライブラリの実装はCで行われているため、実際には可変長引数で実装されています。
https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/mq_open.c
当該部分を引用すると、
c
1mqd_t
2__mq_open (const char *name, int oflag, ...)
3{
4 if (name[0] != '/')
5 return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
6
7 mode_t mode = 0;
8 struct mq_attr *attr = NULL;
9 if (oflag & O_CREAT)
10 {
11 va_list ap;
12
13 va_start (ap, oflag);
14 mode = va_arg (ap, mode_t);
15 attr = va_arg (ap, struct mq_attr *);
16 va_end (ap);
17 }
と、なっています。Cの可変長引数は仕組み的に引数の個数を知る手段がないため(通常は固定引数部分に引数の数を入れたり、フォーマット指定文字列にしたりします)、ここでは、oflag
にO_CREAT
が指定された場合だけ、va_arg
を使って追加で2つの引数を読み取るように実装しています。
しかし、ophiacodonさんのコードでは、O_CREATE
を指定してるのに2つの追加引数がないため、あらぬメモリ領域をmode
やattr
の値として読み取ってしまいます。運良く読み取れるメモリ領域であれば、不正な値を使ってコードが動き、そのまま動いてしまったり(C++のとき)、エラーになったりする(Cのとき)、ということです。場合によっては、読み取れない領域を参照することもあるかもしれません。その場合は、segmentation faultで落ちてしまいます。
対策
mq_openを以下のように書き換えます(引数mode
と引数attr
を追加で指定しています)。
c
1 mqd_t q = mq_open("/sample", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, NULL);