diff --git a/profile/legacy_java_profile.go b/profile/legacy_java_profile.go index db2ee567..7b40f5d2 100644 --- a/profile/legacy_java_profile.go +++ b/profile/legacy_java_profile.go @@ -178,10 +178,15 @@ func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*L // Java profiles have data/fields inverted compared to other // profile types. - value1, value2, addrs := sample[2], sample[1], sample[3] + var err error + value1, value2, value3 := sample[2], sample[1], sample[3] + addrs, err := parseHexAddresses(value3) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } var sloc []*Location - for _, addr := range parseHexAddresses(addrs) { + for _, addr := range addrs { loc := locs[addr] if locs[addr] == nil { loc = &Location{ @@ -197,7 +202,6 @@ func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*L Location: sloc, } - var err error if s.Value[0], err = strconv.ParseInt(value1, 0, 64); err != nil { return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) } diff --git a/profile/legacy_profile.go b/profile/legacy_profile.go index a0abe30c..096890d9 100644 --- a/profile/legacy_profile.go +++ b/profile/legacy_profile.go @@ -622,32 +622,29 @@ func parseHeapSample(line string, rate int64, sampling string, includeAlloc bool return nil, 0, nil, err } - addrs = parseHexAddresses(sampleData[5]) + addrs, err = parseHexAddresses(sampleData[5]) + if err != nil { + return nil, 0, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } return value, blocksize, addrs, nil } -// extractHexAddresses extracts hex numbers from a string and returns -// them, together with their numeric value, in a slice. -func extractHexAddresses(s string) ([]string, []uint64) { +// parseHexAddresses extracts hex numbers from a string, attempts to convert +// each to an unsigned 64-bit number and returns the resulting numbers as a +// slice, or an error if the string contains hex numbers which are too large to +// handle (which means a malformed profile). +func parseHexAddresses(s string) ([]uint64, error) { hexStrings := hexNumberRE.FindAllString(s, -1) - var ids []uint64 + var addrs []uint64 for _, s := range hexStrings { - if id, err := strconv.ParseUint(s, 0, 64); err == nil { - ids = append(ids, id) + if addr, err := strconv.ParseUint(s, 0, 64); err == nil { + addrs = append(addrs, addr) } else { - // Do not expect any parsing failures due to the regexp matching. - panic("failed to parse hex value:" + s) + return nil, fmt.Errorf("failed to parse as hex 64-bit number: %s", s) } } - return hexStrings, ids -} - -// parseHexAddresses parses hex numbers from a string and returns them -// in a slice. -func parseHexAddresses(s string) []uint64 { - _, ids := extractHexAddresses(s) - return ids + return addrs, nil } // scaleHeapSample adjusts the data from a heapz Sample to @@ -803,16 +800,16 @@ func parseContention(b []byte) (*Profile, error) { func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { sampleData := contentionSampleRE.FindStringSubmatch(line) if sampleData == nil { - return value, addrs, errUnrecognized + return nil, nil, errUnrecognized } v1, err := strconv.ParseInt(sampleData[1], 10, 64) if err != nil { - return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) } v2, err := strconv.ParseInt(sampleData[2], 10, 64) if err != nil { - return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) } // Unsample values if period and cpuHz are available. @@ -827,7 +824,10 @@ func parseContentionSample(line string, period, cpuHz int64) (value []int64, add } value = []int64{v2, v1} - addrs = parseHexAddresses(sampleData[3]) + addrs, err = parseHexAddresses(sampleData[3]) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } return value, addrs, nil } @@ -872,7 +872,7 @@ func parseThread(b []byte) (*Profile, error) { var err error line, addrs, err = parseThreadSample(s) if err != nil { - return nil, errUnrecognized + return nil, err } if len(addrs) == 0 { // We got a --same as previous threads--. Bump counters. @@ -936,7 +936,11 @@ func parseThreadSample(s *bufio.Scanner) (nextl string, addrs []uint64, err erro continue } - addrs = append(addrs, parseHexAddresses(line)...) + curAddrs, err := parseHexAddresses(line) + if err != nil { + return "", nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + addrs = append(addrs, curAddrs...) } if err := s.Err(); err != nil { return "", nil, err diff --git a/profile/legacy_profile_test.go b/profile/legacy_profile_test.go index 19acdbed..5f63453e 100644 --- a/profile/legacy_profile_test.go +++ b/profile/legacy_profile_test.go @@ -265,6 +265,21 @@ func TestParseMappingEntry(t *testing.T) { } } +func TestParseThreadProfileWithInvalidAddress(t *testing.T) { + profile := ` +--- threadz 1 --- + +--- Thread 7eff063d9940 (name: main/25376) stack: --- + PC: 0x40b688 0x4d5f51 0x40be31 0x473add693e639c6f0 +--- Memory map: --- + 00400000-00fcb000: /home/rsilvera/cppbench/cppbench_server_main.unstripped + ` + wantErr := "failed to parse as hex 64-bit number: 0x473add693e639c6f0" + if _, gotErr := parseThread([]byte(profile)); !strings.Contains(gotErr.Error(), wantErr) { + t.Errorf("parseThread(): got error %q, want error containing %q", gotErr, wantErr) + } +} + func TestParseGoCount(t *testing.T) { for _, test := range []struct { in string