Random segmentation fault in D lang on switch break

246 Views Asked by At

I was debugging a fairly simple program written in D, that seems to have a random chance to receive a SEGV signal. Upon further inspection I observed that using different compilers and build modes yielded different results.

Results of my tests:

  • DMD Debug = works 99% of the time
  • DMD Release = 50/50
  • LDC Debug = 50/50
  • LDC Release = 50/50

Because the binary from the default compiler (DMD) crashed only once I couldn't really debug it, and release mode didn't help either due to lack of debug symbols. Building the binary with LDC in debug mode let me test it with gdb and valgrind, to summarize what I gathered.

Relevant information from valgrind,

Invalid read of size 4 @ ctor in file video.d line 46

Access not within mapped region at address 0x0 @ ctor in file video.d line 

Gdb doesn't give me any more insight, 3 stack frames, of which only 0th is of interest, backtrace of frame 0 shows file video.d line 46 which is a break statement, so what now?

This is the snippet of code producing a seg fault

module video;

import ffmpeg.libavformat.avformat;
import ffmpeg.libavcodec.avcodec; 
import ffmpeg.libavutil.avutil;

class Foo
{
    private
    {
        AVFormatContext* _format_ctx;
        AVStream* _stream_video;
        AVStream* _stream_audio;
    }

    ...

    public this(const(string) path)
    {
        import std.string : toStringz;

        _format_ctx = null;
        enforce(avformat_open_input(&_format_ctx, path.toStringz, null, null) == 0);
        scope (failure) avformat_close_input(&_format_ctx);

        enforce(avformat_find_stream_info(_format_ctx, null) == 0);
        debug av_dump_format(_format_ctx, 0, path.toStringz, 0);

        foreach (i; 0 .. _format_ctx.nb_streams)
        {
            AVStream* stream = _format_ctx.streams[i];

            if (stream == null)
                continue;

            enforce (stream.codecpar != null);

            switch (stream.codecpar.codec_type)
            {
                case AVMediaType.AVMEDIA_TYPE_VIDEO:
                    _stream_video = stream;
                    break;
                case AVMediaType.AVMEDIA_TYPE_AUDIO:
                    _stream_audio = stream;
                    break;
                default:
                    stream.discard = AVDiscard.AVDISCARD_ALL;
                    break; // Magic line 46
            }
        }
    }
}

// Might contain spelling errors, had to write it by hand.

So does anyone have an idea what causes this behaviour, or more precisely how to go about fixing it?

2

There are 2 best solutions below

2
On

Try to check validity _stream_audio

default:
    enforce( _stream_audio, new Exception( "_stream_audio is null" ))
        .discard = AVDiscard.AVDISCARD_ALL;
    break; // Magic line 46
0
On

You are not abiding the warning in the toStringz documentation:

“Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.”

This may not be the cause of your problem, but the way you use toStringz is risky.