Skip to content
Merged
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
22 changes: 11 additions & 11 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ name: Go

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Set up Go
uses: actions/setup-go@v2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Code Vulnerability

Workflow depends on a GitHub actions pinned by tag instead of a hash. (...read more)

Pin GitHub Actions by commit hash to ensure supply chain security.

Using a branch (@main) or tag (@v1) allows for implicit updates, which can introduce unexpected or malicious changes. Instead, always pin actions to a full length commit SHA. You can find the commit SHA for the latest tag from the action’s repository and ensure frequent updates via auto-updaters such as dependabot. Include a comment with the corresponding full-length SemVer tag for clarity:

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

View in Datadog  Leave us feedback  Documentation

with:
go-version: 1.24

- name: Build
run: go build -v ./...
- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...
- name: Test
run: go test -v ./...
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
module github.com/DataDog/gostackparse

go 1.16
go 1.18

require github.com/stretchr/testify v1.7.0

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
7 changes: 6 additions & 1 deletion gostackparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ func Parse(r io.Reader) ([]*Goroutine, []error) {
goroutines = append(goroutines, g)
}
if state == stateOriginatingFrom {
ancestorIDStr := line[len(originatingFromPrefix) : len(line)-2]
// Make sure we capture only the ID that we need. If it's truncated,
// it'll be handled by the error check below
ancestorIDStr := bytes.TrimSuffix(
bytes.TrimPrefix(line, originatingFromPrefix),
[]byte("]:"),
)
ancestorID, err := strconv.Atoi(string(ancestorIDStr))
if err != nil {
abortGoroutine(err.Error())
Expand Down
44 changes: 44 additions & 0 deletions gostackparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package gostackparse

import (
"bytes"
"embed"
"encoding/json"
"flag"
"fmt"
Expand Down Expand Up @@ -361,6 +362,49 @@ func BenchmarkGostackparse(b *testing.B) {
b.ReportMetric(mbPerSec, "MiB/s")
}

// Using go:embed to load test fixtures into memory, so the internal fuzzing infra doesn't need to handle individual files
// along with the fuzzer binary.
//
//go:embed test-fixtures/*.txt
var testFixtures embed.FS

func FuzzParse(f *testing.F) {
files, err := testFixtures.ReadDir("test-fixtures")
require.NoError(f, err)

for _, file := range files {
if strings.HasSuffix(file.Name(), ".txt") {
body, err := testFixtures.ReadFile(filepath.Join("test-fixtures", file.Name()))
require.NoError(f, err)
f.Add(body)
}
}
// Regression tests
// panic: runtime error: slice bounds out of range [28:26]
f.Add([]byte("goroutine 0 [0]:\n0()\n\t:0\n[originating from goroutine "))

f.Fuzz(func(t *testing.T, data []byte) {
gs, err := Parse(bytes.NewReader(data))
if err != nil {
t.Skip()
}
// Invariant checks
for _, g := range gs {
for _, f := range g.Stack {
if f.Func == "" {
t.Errorf("func is empty: %+v", f)
}
}
}
})
}

// This is a regression test for a panic on a truncated line with an [originating from goroutine prefix.
func TestCrashRegression(t *testing.T) {
crashPayload := []byte("goroutine 0 [0]:\n0()\n\t:0\n[originating from goroutine ")
_, _ = Parse(bytes.NewReader(crashPayload))
}

func TestFuzzCorupus(t *testing.T) {
if os.Getenv("FUZZ_CORPUS") == "" {
t.Skip("set FUZZ_CORPUS=true to generate fuzz corpus")
Expand Down