Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix panic on malformed playlists #203

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ var reKeyValue = regexp.MustCompile(`([a-zA-Z0-9_-]+)=("[^"]+"|[^",]+)`)

// TimeParse allows globally apply and/or override Time Parser function.
// Available variants:
// * FullTimeParse - implements full featured ISO/IEC 8601:2004
// * StrictTimeParse - implements only RFC3339 Nanoseconds format
// - FullTimeParse - implements full featured ISO/IEC 8601:2004
// - StrictTimeParse - implements only RFC3339 Nanoseconds format
var TimeParse func(value string) (time.Time, error) = FullTimeParse

// Decode parses a master playlist passed from the buffer. If `strict`
Expand Down Expand Up @@ -536,7 +536,9 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
}
// If EXT-X-KEY appeared before reference to segment (EXTINF) then it linked to this segment
if state.tagKey {
p.Segments[p.last()].Key = &Key{state.xkey.Method, state.xkey.URI, state.xkey.IV, state.xkey.Keyformat, state.xkey.Keyformatversions}
if segment := p.Segments[p.last()]; segment != nil {
segment.Key = &Key{state.xkey.Method, state.xkey.URI, state.xkey.IV, state.xkey.Keyformat, state.xkey.Keyformatversions}
}
// First EXT-X-KEY may appeared in the header of the playlist and linked to first segment
// but for convenient playlist generation it also linked as default playlist key
if p.Key == nil {
Expand All @@ -546,7 +548,9 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
}
// If EXT-X-MAP appeared before reference to segment (EXTINF) then it linked to this segment
if state.tagMap {
p.Segments[p.last()].Map = &Map{state.xmap.URI, state.xmap.Limit, state.xmap.Offset}
if segment := p.Segments[p.last()]; segment != nil {
segment.Map = &Map{state.xmap.URI, state.xmap.Limit, state.xmap.Offset}
}
// First EXT-X-MAP may appeared in the header of the playlist and linked to first segment
// but for convenient playlist generation it also linked as default playlist map
if p.Map == nil {
Expand All @@ -557,7 +561,9 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l

// if segment custom tag appeared before EXTINF then it links to this segment
if state.tagCustom {
p.Segments[p.last()].Custom = state.custom
if segment := p.Segments[p.last()]; segment != nil {
segment.Custom = state.custom
}
state.custom = make(map[string]CustomTag)
state.tagCustom = false
}
Expand Down Expand Up @@ -759,7 +765,7 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
if err == nil {
state.tagWV = true
}
case strings.HasPrefix(line, "#WV-CYPHER-VERSION"):
case strings.HasPrefix(line, "#WV-CYPHER-VERSION "):
state.listType = MEDIA
wv.CypherVersion = line[19:]
state.tagWV = true
Expand Down Expand Up @@ -803,7 +809,7 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
if err == nil {
state.tagWV = true
}
case strings.HasPrefix(line, "#WV-VIDEO-RESOLUTION"):
case strings.HasPrefix(line, "#WV-VIDEO-RESOLUTION "):
state.listType = MEDIA
wv.VideoResolution = line[21:]
state.tagWV = true
Expand Down
117 changes: 112 additions & 5 deletions reader_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Playlist parsing tests.
Playlist parsing tests.

Copyright 2013-2019 The Project Developers.
See the AUTHORS and LICENSE files at the top-level directory of this distribution
and at https://github.com/grafov/m3u8/
Copyright 2013-2019 The Project Developers.
See the AUTHORS and LICENSE files at the top-level directory of this distribution
and at https://github.com/grafov/m3u8/

ॐ तारे तुत्तारे तुरे स्व
ॐ तारे तुत्तारे तुरे स्व
*/
package m3u8

Expand Down Expand Up @@ -972,6 +972,113 @@ func TestDecodeMediaPlaylistStartTime(t *testing.T) {
}
}

/********************
* Bad data tests *
********************/

// Test mallformed playlist
func TestMalformedMasterPlaylis(t *testing.T) {
data := []byte("#EXT-X-START:\n#EXTM3U")
_, _, err := DecodeFrom(bytes.NewReader(data), true)
if err != nil {
if !errors.Is(err, ErrorNoEXTM3U) {
t.Errorf("Wrong error type at DecodeFrom: %s", err)
}
} else {
t.Error("No error on malformed playlist on DecodeFrome")
}
if _, _, err := DecodeFrom(bytes.NewReader(data), false); err != nil {
t.Errorf("Unexpected error on not strict mode of parsing: %s", err)
}

var master = new(MasterPlaylist)
err = master.DecodeFrom(bytes.NewReader(data), true)
if err != nil {
if !errors.Is(err, ErrorNoEXTM3U) {
t.Errorf("Wrong error type at (*MasterPlaylist).DecodeFrom: %s",
err)
}
} else {
t.Error("No error on malformed playlist on (*MasterPlaylist).DecodeFrome")
}

if err := master.DecodeFrom(bytes.NewReader(data), false); err != nil {
t.Errorf("Unexpected error on not strict mode of parsing: %s", err)
}

var media = new(MediaPlaylist)
err = media.DecodeFrom(bytes.NewReader(data), true)
if err != nil {
if !errors.Is(err, ErrorNoEXTM3U) {
t.Errorf("Wrong error type at (*MediaPlaylist).DecodeFrom: %s",
err)
}
} else {
// TODO: There is probably an error here. The
// tag EXT-X-START should only appear in the master playlist.
t.Error("No error on malformed playlist on (*MediaPlaylist).DecodeFrome")
}
if err := media.DecodeFrom(bytes.NewReader(data), false); err != nil {
t.Errorf("Unexpected error on not strict mode of parsing: %s", err)
}

}

func TestDecodeMediaPlaylistDicontinuityAtBegin(t *testing.T) {
f, err := os.Open("sample-playlists/media-with-discontinuity-at-start.m3u8")
if err != nil {
t.Fatal(err)
}
p, listType, err := DecodeFrom(bufio.NewReader(f), true)
if err != nil {
t.Fatal(err)
}
pp := p.(*MediaPlaylist)
CheckType(t, pp)
if listType != MEDIA {
t.Error("Sample not recognized as media playlist.")
}
if pp.StartTime != float64(0.0) {
t.Errorf("Media segment StartTime != 0: %f", pp.StartTime)
}
}

// Test for https://github.com/khenarghot/m3u8/issues/3
func TestMellformedPanicIssue3(t *testing.T) {
bad := bytes.NewBuffer([]byte(`#WV-CYPHER-VERSION`))
_, _, err := DecodeFrom(bad, true)
if err == nil {
t.Fail()
}
}

// Test for https://github.com/khenarghot/m3u8/issues/1
func TestMellformedPanicIssue1(t *testing.T) {
bad := bytes.NewBuffer([]byte(`#WV-VIDEO-RESOLUTION`))
_, _, err := DecodeFrom(bad, true)
if err == nil {
t.Fail()
}
}

// Test for https://github.com/khenarghot/m3u8/issues/2
func TestMellformedPanicIssue2(t *testing.T) {
bad := bytes.NewBuffer([]byte("#EXT-X-KEY:\n0"))
_, _, err := DecodeFrom(bad, false)
if err != nil {
t.Fail()
}
}

// Test for https://github.com/khenarghot/m3u8/issues/2
func TestMellformedPanicIssue2AltMAP(t *testing.T) {
bad := bytes.NewBuffer([]byte("#EXT-X-MAP:\n0"))
_, _, err := DecodeFrom(bad, false)
if err != nil {
t.Fail()
}
}

/****************
* Benchmarks *
****************/
Expand Down